mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-03 17:26:07 +00:00
Merge remote-tracking branch 'origin/main' into misc/privilege-management-v2-transition
This commit is contained in:
Generated
+99
-67
@@ -31,7 +31,7 @@
|
|||||||
"@fastify/swagger-ui": "^2.1.0",
|
"@fastify/swagger-ui": "^2.1.0",
|
||||||
"@google-cloud/kms": "^4.5.0",
|
"@google-cloud/kms": "^4.5.0",
|
||||||
"@infisical/quic": "^1.0.8",
|
"@infisical/quic": "^1.0.8",
|
||||||
"@node-saml/passport-saml": "^4.0.4",
|
"@node-saml/passport-saml": "^5.0.1",
|
||||||
"@octokit/auth-app": "^7.1.1",
|
"@octokit/auth-app": "^7.1.1",
|
||||||
"@octokit/plugin-retry": "^5.0.5",
|
"@octokit/plugin-retry": "^5.0.5",
|
||||||
"@octokit/rest": "^20.0.2",
|
"@octokit/rest": "^20.0.2",
|
||||||
@@ -6747,32 +6747,35 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@node-saml/node-saml": {
|
"node_modules/@node-saml/node-saml": {
|
||||||
"version": "4.0.5",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@node-saml/node-saml/-/node-saml-4.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@node-saml/node-saml/-/node-saml-5.0.1.tgz",
|
||||||
"integrity": "sha512-J5DglElbY1tjOuaR1NPtjOXkXY5bpUhDoKVoeucYN98A3w4fwgjIOPqIGcb6cQsqFq2zZ6vTCeKn5C/hvefSaw==",
|
"integrity": "sha512-YQzFPEC+CnsfO9AFYnwfYZKIzOLx3kITaC1HrjHVLTo6hxcQhc+LgHODOMvW4VCV95Gwrz1MshRUWCPzkDqmnA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/debug": "^4.1.7",
|
"@types/debug": "^4.1.12",
|
||||||
"@types/passport": "^1.0.11",
|
"@types/qs": "^6.9.11",
|
||||||
"@types/xml-crypto": "^1.4.2",
|
"@types/xml-encryption": "^1.2.4",
|
||||||
"@types/xml-encryption": "^1.2.1",
|
"@types/xml2js": "^0.4.14",
|
||||||
"@types/xml2js": "^0.4.11",
|
"@xmldom/is-dom-node": "^1.0.1",
|
||||||
"@xmldom/xmldom": "^0.8.6",
|
"@xmldom/xmldom": "^0.8.10",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"xml-crypto": "^3.0.1",
|
"xml-crypto": "^6.0.1",
|
||||||
"xml-encryption": "^3.0.2",
|
"xml-encryption": "^3.0.2",
|
||||||
"xml2js": "^0.5.0",
|
"xml2js": "^0.6.2",
|
||||||
"xmlbuilder": "^15.1.1"
|
"xmlbuilder": "^15.1.1",
|
||||||
|
"xpath": "^0.0.34"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 14"
|
"node": ">= 18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@node-saml/node-saml/node_modules/debug": {
|
"node_modules/@node-saml/node-saml/node_modules/debug": {
|
||||||
"version": "4.3.4",
|
"version": "4.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": "2.1.2"
|
"ms": "^2.1.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.0"
|
"node": ">=6.0"
|
||||||
@@ -6783,25 +6786,43 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@node-saml/node-saml/node_modules/ms": {
|
"node_modules/@node-saml/node-saml/node_modules/xml2js": {
|
||||||
"version": "2.1.2",
|
"version": "0.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz",
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
"integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"sax": ">=0.6.0",
|
||||||
|
"xmlbuilder": "~11.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@node-saml/node-saml/node_modules/xml2js/node_modules/xmlbuilder": {
|
||||||
|
"version": "11.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
|
||||||
|
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@node-saml/passport-saml": {
|
"node_modules/@node-saml/passport-saml": {
|
||||||
"version": "4.0.4",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@node-saml/passport-saml/-/passport-saml-4.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@node-saml/passport-saml/-/passport-saml-5.0.1.tgz",
|
||||||
"integrity": "sha512-xFw3gw0yo+K1mzlkW15NeBF7cVpRHN/4vpjmBKzov5YFImCWh/G0LcTZ8krH3yk2/eRPc3Or8LRPudVJBjmYaw==",
|
"integrity": "sha512-fMztg3zfSnjLEgxvpl6HaDMNeh0xeQX4QHiF9e2Lsie2dc4qFE37XYbQZhVmn8XJ2awPpSWLQ736UskYgGU8lQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@node-saml/node-saml": "^4.0.4",
|
"@node-saml/node-saml": "^5.0.1",
|
||||||
"@types/express": "^4.17.14",
|
"@types/express": "^4.17.21",
|
||||||
"@types/passport": "^1.0.11",
|
"@types/passport": "^1.0.16",
|
||||||
"@types/passport-strategy": "^0.2.35",
|
"@types/passport-strategy": "^0.2.38",
|
||||||
"passport": "^0.6.0",
|
"passport": "^0.7.0",
|
||||||
"passport-strategy": "^1.0.0"
|
"passport-strategy": "^1.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 14"
|
"node": ">= 18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
@@ -9606,6 +9627,7 @@
|
|||||||
"version": "4.1.12",
|
"version": "4.1.12",
|
||||||
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
|
||||||
"integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
|
"integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/ms": "*"
|
"@types/ms": "*"
|
||||||
}
|
}
|
||||||
@@ -9725,9 +9747,10 @@
|
|||||||
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="
|
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="
|
||||||
},
|
},
|
||||||
"node_modules/@types/ms": {
|
"node_modules/@types/ms": {
|
||||||
"version": "0.7.34",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz",
|
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
|
||||||
"integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
|
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "20.9.5",
|
"version": "20.9.5",
|
||||||
@@ -9907,9 +9930,10 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/qs": {
|
"node_modules/@types/qs": {
|
||||||
"version": "6.9.10",
|
"version": "6.9.18",
|
||||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz",
|
||||||
"integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw=="
|
"integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/range-parser": {
|
"node_modules/@types/range-parser": {
|
||||||
"version": "1.2.7",
|
"version": "1.2.7",
|
||||||
@@ -10058,19 +10082,11 @@
|
|||||||
"@types/webidl-conversions": "*"
|
"@types/webidl-conversions": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/xml-crypto": {
|
|
||||||
"version": "1.4.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/xml-crypto/-/xml-crypto-1.4.6.tgz",
|
|
||||||
"integrity": "sha512-A6jEW2FxLZo1CXsRWnZHUX2wzR3uDju2Bozt6rDbSmU/W8gkilaVbwFEVN0/NhnUdMVzwYobWtM6bU1QJJFb7Q==",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/node": "*",
|
|
||||||
"xpath": "0.0.27"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/xml-encryption": {
|
"node_modules/@types/xml-encryption": {
|
||||||
"version": "1.2.4",
|
"version": "1.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/xml-encryption/-/xml-encryption-1.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/xml-encryption/-/xml-encryption-1.2.4.tgz",
|
||||||
"integrity": "sha512-I69K/WW1Dv7j6O3jh13z0X8sLWJRXbu5xnHDl9yHzUNDUBtUoBY058eb5s+x/WG6yZC1h8aKdI2EoyEPjyEh+Q==",
|
"integrity": "sha512-I69K/WW1Dv7j6O3jh13z0X8sLWJRXbu5xnHDl9yHzUNDUBtUoBY058eb5s+x/WG6yZC1h8aKdI2EoyEPjyEh+Q==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
@@ -10079,6 +10095,7 @@
|
|||||||
"version": "0.4.14",
|
"version": "0.4.14",
|
||||||
"resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.14.tgz",
|
"resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.14.tgz",
|
||||||
"integrity": "sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==",
|
"integrity": "sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
@@ -10522,10 +10539,20 @@
|
|||||||
"url": "https://opencollective.com/vitest"
|
"url": "https://opencollective.com/vitest"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@xmldom/is-dom-node": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@xmldom/is-dom-node/-/is-dom-node-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-CJDxIgE5I0FH+ttq/Fxy6nRpxP70+e2O048EPe85J2use3XKdatVM7dDVvFNjQudd9B49NPoZ+8PG49zj4Er8Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@xmldom/xmldom": {
|
"node_modules/@xmldom/xmldom": {
|
||||||
"version": "0.8.10",
|
"version": "0.8.10",
|
||||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
|
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
|
||||||
"integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==",
|
"integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
}
|
}
|
||||||
@@ -18222,9 +18249,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/passport": {
|
"node_modules/passport": {
|
||||||
"version": "0.6.0",
|
"version": "0.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz",
|
||||||
"integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==",
|
"integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"passport-strategy": "1.x.x",
|
"passport-strategy": "1.x.x",
|
||||||
"pause": "0.0.1",
|
"pause": "0.0.1",
|
||||||
@@ -23692,42 +23720,44 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/xml-crypto": {
|
"node_modules/xml-crypto": {
|
||||||
"version": "3.2.0",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/xml-crypto/-/xml-crypto-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/xml-crypto/-/xml-crypto-6.0.1.tgz",
|
||||||
"integrity": "sha512-qVurBUOQrmvlgmZqIVBqmb06TD2a/PpEUfFPgD7BuBfjmoH4zgkqaWSIJrnymlCvM2GGt9x+XtJFA+ttoAufqg==",
|
"integrity": "sha512-v05aU7NS03z4jlZ0iZGRFeZsuKO1UfEbbYiaeRMiATBFs6Jq9+wqKquEMTn4UTrYZ9iGD8yz3KT4L9o2iF682w==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@xmldom/xmldom": "^0.8.8",
|
"@xmldom/is-dom-node": "^1.0.1",
|
||||||
"xpath": "0.0.32"
|
"@xmldom/xmldom": "^0.8.10",
|
||||||
|
"xpath": "^0.0.33"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4.0.0"
|
"node": ">=16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/xml-crypto/node_modules/xpath": {
|
"node_modules/xml-crypto/node_modules/xpath": {
|
||||||
"version": "0.0.32",
|
"version": "0.0.33",
|
||||||
"resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.32.tgz",
|
"resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.33.tgz",
|
||||||
"integrity": "sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw==",
|
"integrity": "sha512-NNXnzrkDrAzalLhIUc01jO2mOzXGXh1JwPgkihcLLzw98c0WgYDmmjSh1Kl3wzaxSVWMuA+fe0WTWOBDWCBmNA==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.6.0"
|
"node": ">=0.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/xml-encryption": {
|
"node_modules/xml-encryption": {
|
||||||
"version": "3.0.2",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/xml-encryption/-/xml-encryption-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/xml-encryption/-/xml-encryption-3.1.0.tgz",
|
||||||
"integrity": "sha512-VxYXPvsWB01/aqVLd6ZMPWZ+qaj0aIdF+cStrVJMcFj3iymwZeI0ABzB3VqMYv48DkSpRhnrXqTUkR34j+UDyg==",
|
"integrity": "sha512-PV7qnYpoAMXbf1kvQkqMScLeQpjCMixddAKq9PtqVrho8HnYbBOWNfG0kA4R7zxQDo7w9kiYAyzS/ullAyO55Q==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@xmldom/xmldom": "^0.8.5",
|
"@xmldom/xmldom": "^0.8.5",
|
||||||
"escape-html": "^1.0.3",
|
"escape-html": "^1.0.3",
|
||||||
"xpath": "0.0.32"
|
"xpath": "0.0.32"
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/xml-encryption/node_modules/xpath": {
|
"node_modules/xml-encryption/node_modules/xpath": {
|
||||||
"version": "0.0.32",
|
"version": "0.0.32",
|
||||||
"resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.32.tgz",
|
"resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.32.tgz",
|
||||||
"integrity": "sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw==",
|
"integrity": "sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.6.0"
|
"node": ">=0.6.0"
|
||||||
}
|
}
|
||||||
@@ -23764,6 +23794,7 @@
|
|||||||
"version": "15.1.1",
|
"version": "15.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz",
|
||||||
"integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==",
|
"integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.0"
|
"node": ">=8.0"
|
||||||
}
|
}
|
||||||
@@ -23774,9 +23805,10 @@
|
|||||||
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
|
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
|
||||||
},
|
},
|
||||||
"node_modules/xpath": {
|
"node_modules/xpath": {
|
||||||
"version": "0.0.27",
|
"version": "0.0.34",
|
||||||
"resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.27.tgz",
|
"resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.34.tgz",
|
||||||
"integrity": "sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ==",
|
"integrity": "sha512-FxF6+rkr1rNSQrhUNYrAFJpRXNzlDoMxeXN5qI84939ylEv3qqPFKa85Oxr6tDaJKqwW6KKyo2v26TSv3k6LeA==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.6.0"
|
"node": ">=0.6.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,7 +148,7 @@
|
|||||||
"@fastify/swagger-ui": "^2.1.0",
|
"@fastify/swagger-ui": "^2.1.0",
|
||||||
"@google-cloud/kms": "^4.5.0",
|
"@google-cloud/kms": "^4.5.0",
|
||||||
"@infisical/quic": "^1.0.8",
|
"@infisical/quic": "^1.0.8",
|
||||||
"@node-saml/passport-saml": "^4.0.4",
|
"@node-saml/passport-saml": "^5.0.1",
|
||||||
"@octokit/auth-app": "^7.1.1",
|
"@octokit/auth-app": "^7.1.1",
|
||||||
"@octokit/plugin-retry": "^5.0.5",
|
"@octokit/plugin-retry": "^5.0.5",
|
||||||
"@octokit/rest": "^20.0.2",
|
"@octokit/rest": "^20.0.2",
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
import "@fastify/request-context";
|
|
||||||
|
|
||||||
declare module "@fastify/request-context" {
|
|
||||||
interface RequestContextData {
|
|
||||||
reqId: string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Vendored
+6
@@ -100,6 +100,12 @@ import { TWorkflowIntegrationServiceFactory } from "@app/services/workflow-integ
|
|||||||
declare module "@fastify/request-context" {
|
declare module "@fastify/request-context" {
|
||||||
interface RequestContextData {
|
interface RequestContextData {
|
||||||
reqId: string;
|
reqId: string;
|
||||||
|
identityAuthInfo?: {
|
||||||
|
identityId: string;
|
||||||
|
oidc?: {
|
||||||
|
claims: Record<string, string>;
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (await knex.schema.hasTable(TableName.DynamicSecret)) {
|
if (await knex.schema.hasTable(TableName.DynamicSecret)) {
|
||||||
const doesGatewayColExist = await knex.schema.hasColumn(TableName.DynamicSecret, "gatewayId");
|
const doesGatewayColExist = await knex.schema.hasColumn(TableName.DynamicSecret, "projectGatewayId");
|
||||||
await knex.schema.alterTable(TableName.DynamicSecret, (t) => {
|
await knex.schema.alterTable(TableName.DynamicSecret, (t) => {
|
||||||
// not setting a foreign constraint so that cascade effects are not triggered
|
// not setting a foreign constraint so that cascade effects are not triggered
|
||||||
if (!doesGatewayColExist) {
|
if (!doesGatewayColExist) {
|
||||||
|
|||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasTable(TableName.Organization)) {
|
||||||
|
const hasSecretShareToAnyoneCol = await knex.schema.hasColumn(
|
||||||
|
TableName.Organization,
|
||||||
|
"allowSecretSharingOutsideOrganization"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasSecretShareToAnyoneCol) {
|
||||||
|
await knex.schema.alterTable(TableName.Organization, (t) => {
|
||||||
|
t.boolean("allowSecretSharingOutsideOrganization").defaultTo(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasTable(TableName.Organization)) {
|
||||||
|
const hasSecretShareToAnyoneCol = await knex.schema.hasColumn(
|
||||||
|
TableName.Organization,
|
||||||
|
"allowSecretSharingOutsideOrganization"
|
||||||
|
);
|
||||||
|
if (hasSecretShareToAnyoneCol) {
|
||||||
|
await knex.schema.alterTable(TableName.Organization, (t) => {
|
||||||
|
t.dropColumn("allowSecretSharingOutsideOrganization");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const hasMappingField = await knex.schema.hasColumn(TableName.IdentityOidcAuth, "claimMetadataMapping");
|
||||||
|
if (!hasMappingField) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityOidcAuth, (t) => {
|
||||||
|
t.jsonb("claimMetadataMapping");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const hasMappingField = await knex.schema.hasColumn(TableName.IdentityOidcAuth, "claimMetadataMapping");
|
||||||
|
if (hasMappingField) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityOidcAuth, (t) => {
|
||||||
|
t.dropColumn("claimMetadataMapping");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,7 +26,8 @@ export const IdentityOidcAuthsSchema = z.object({
|
|||||||
boundSubject: z.string().nullable().optional(),
|
boundSubject: z.string().nullable().optional(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
encryptedCaCertificate: zodBuffer.nullable().optional()
|
encryptedCaCertificate: zodBuffer.nullable().optional(),
|
||||||
|
claimMetadataMapping: z.unknown().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TIdentityOidcAuths = z.infer<typeof IdentityOidcAuthsSchema>;
|
export type TIdentityOidcAuths = z.infer<typeof IdentityOidcAuthsSchema>;
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ export const OrganizationsSchema = z.object({
|
|||||||
selectedMfaMethod: z.string().nullable().optional(),
|
selectedMfaMethod: z.string().nullable().optional(),
|
||||||
shouldUseNewPrivilegeSystem: z.boolean().default(true),
|
shouldUseNewPrivilegeSystem: z.boolean().default(true),
|
||||||
privilegeUpgradeInitiatedByUsername: z.string().nullable().optional(),
|
privilegeUpgradeInitiatedByUsername: z.string().nullable().optional(),
|
||||||
privilegeUpgradeInitiatedAt: z.date().nullable().optional()
|
privilegeUpgradeInitiatedAt: z.date().nullable().optional(),
|
||||||
|
allowSecretSharingOutsideOrganization: z.boolean().default(true).nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TOrganizations = z.infer<typeof OrganizationsSchema>;
|
export type TOrganizations = z.infer<typeof OrganizationsSchema>;
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import { TImmutableDBKeys } from "./models";
|
|||||||
export const SecretSharingSchema = z.object({
|
export const SecretSharingSchema = z.object({
|
||||||
id: z.string().uuid(),
|
id: z.string().uuid(),
|
||||||
encryptedValue: z.string().nullable().optional(),
|
encryptedValue: z.string().nullable().optional(),
|
||||||
type: z.string(),
|
|
||||||
iv: z.string().nullable().optional(),
|
iv: z.string().nullable().optional(),
|
||||||
tag: z.string().nullable().optional(),
|
tag: z.string().nullable().optional(),
|
||||||
hashedHex: z.string().nullable().optional(),
|
hashedHex: z.string().nullable().optional(),
|
||||||
@@ -27,7 +26,8 @@ export const SecretSharingSchema = z.object({
|
|||||||
lastViewedAt: z.date().nullable().optional(),
|
lastViewedAt: z.date().nullable().optional(),
|
||||||
password: z.string().nullable().optional(),
|
password: z.string().nullable().optional(),
|
||||||
encryptedSecret: zodBuffer.nullable().optional(),
|
encryptedSecret: zodBuffer.nullable().optional(),
|
||||||
identifier: z.string().nullable().optional()
|
identifier: z.string().nullable().optional(),
|
||||||
|
type: z.string().default("share")
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSecretSharing = z.infer<typeof SecretSharingSchema>;
|
export type TSecretSharing = z.infer<typeof SecretSharingSchema>;
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import ms from "ms";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { DynamicSecretLeasesSchema } from "@app/db/schemas";
|
import { DynamicSecretLeasesSchema } from "@app/db/schemas";
|
||||||
import { DYNAMIC_SECRET_LEASES } from "@app/lib/api-docs";
|
import { DYNAMIC_SECRET_LEASES } from "@app/lib/api-docs";
|
||||||
import { daysToMillisecond } from "@app/lib/dates";
|
import { daysToMillisecond } from "@app/lib/dates";
|
||||||
import { removeTrailingSlash } from "@app/lib/fn";
|
import { removeTrailingSlash } from "@app/lib/fn";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { SanitizedDynamicSecretSchema } from "@app/server/routes/sanitizedSchemas";
|
import { SanitizedDynamicSecretSchema } from "@app/server/routes/sanitizedSchemas";
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import ms from "ms";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { DynamicSecretLeasesSchema } from "@app/db/schemas";
|
import { DynamicSecretLeasesSchema } from "@app/db/schemas";
|
||||||
@@ -6,6 +5,7 @@ import { DynamicSecretProviderSchema } from "@app/ee/services/dynamic-secret/pro
|
|||||||
import { DYNAMIC_SECRETS } from "@app/lib/api-docs";
|
import { DYNAMIC_SECRETS } from "@app/lib/api-docs";
|
||||||
import { daysToMillisecond } from "@app/lib/dates";
|
import { daysToMillisecond } from "@app/lib/dates";
|
||||||
import { removeTrailingSlash } from "@app/lib/fn";
|
import { removeTrailingSlash } from "@app/lib/fn";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { slugSchema } from "@app/server/lib/schemas";
|
import { slugSchema } from "@app/server/lib/schemas";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import ms from "ms";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-types";
|
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-types";
|
||||||
import { backfillPermissionV1SchemaToV2Schema } from "@app/ee/services/permission/project-permission";
|
import { backfillPermissionV1SchemaToV2Schema } from "@app/ee/services/permission/project-permission";
|
||||||
import { IDENTITY_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
import { IDENTITY_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
||||||
import { UnauthorizedError } from "@app/lib/errors";
|
import { UnauthorizedError } from "@app/lib/errors";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { slugSchema } from "@app/server/lib/schemas";
|
import { slugSchema } from "@app/server/lib/schemas";
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import ms from "ms";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { KmipClientsSchema } from "@app/db/schemas";
|
import { KmipClientsSchema } from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { KmipPermission } from "@app/ee/services/kmip/kmip-enum";
|
import { KmipPermission } from "@app/ee/services/kmip/kmip-enum";
|
||||||
import { KmipClientOrderBy } from "@app/ee/services/kmip/kmip-types";
|
import { KmipClientOrderBy } from "@app/ee/services/kmip/kmip-types";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { OrderByDirection } from "@app/lib/types";
|
import { OrderByDirection } from "@app/lib/types";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ type TSAMLConfig = {
|
|||||||
callbackUrl: string;
|
callbackUrl: string;
|
||||||
entryPoint: string;
|
entryPoint: string;
|
||||||
issuer: string;
|
issuer: string;
|
||||||
cert: string;
|
idpCert: string;
|
||||||
audience: string;
|
audience: string;
|
||||||
wantAuthnResponseSigned?: boolean;
|
wantAuthnResponseSigned?: boolean;
|
||||||
wantAssertionsSigned?: boolean;
|
wantAssertionsSigned?: boolean;
|
||||||
@@ -72,7 +72,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
|||||||
callbackUrl: `${appCfg.SITE_URL}/api/v1/sso/saml2/${ssoConfig.id}`,
|
callbackUrl: `${appCfg.SITE_URL}/api/v1/sso/saml2/${ssoConfig.id}`,
|
||||||
entryPoint: ssoConfig.entryPoint,
|
entryPoint: ssoConfig.entryPoint,
|
||||||
issuer: ssoConfig.issuer,
|
issuer: ssoConfig.issuer,
|
||||||
cert: ssoConfig.cert,
|
idpCert: ssoConfig.cert,
|
||||||
audience: appCfg.SITE_URL || ""
|
audience: appCfg.SITE_URL || ""
|
||||||
};
|
};
|
||||||
if (ssoConfig.authProvider === SamlProviders.JUMPCLOUD_SAML) {
|
if (ssoConfig.authProvider === SamlProviders.JUMPCLOUD_SAML) {
|
||||||
@@ -302,15 +302,21 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const saml = await server.services.saml.createSamlCfg({
|
const { isActive, authProvider, issuer, entryPoint, cert } = req.body;
|
||||||
actor: req.permission.type,
|
const { permission } = req;
|
||||||
actorId: req.permission.id,
|
|
||||||
actorAuthMethod: req.permission.authMethod,
|
return server.services.saml.createSamlCfg({
|
||||||
actorOrgId: req.permission.orgId,
|
isActive,
|
||||||
orgId: req.body.organizationId,
|
authProvider,
|
||||||
...req.body
|
issuer,
|
||||||
|
entryPoint,
|
||||||
|
idpCert: cert,
|
||||||
|
actor: permission.type,
|
||||||
|
actorId: permission.id,
|
||||||
|
actorAuthMethod: permission.authMethod,
|
||||||
|
actorOrgId: permission.orgId,
|
||||||
|
orgId: req.body.organizationId
|
||||||
});
|
});
|
||||||
return saml;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -337,15 +343,21 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const saml = await server.services.saml.updateSamlCfg({
|
const { isActive, authProvider, issuer, entryPoint, cert } = req.body;
|
||||||
actor: req.permission.type,
|
const { permission } = req;
|
||||||
actorId: req.permission.id,
|
|
||||||
actorAuthMethod: req.permission.authMethod,
|
return server.services.saml.updateSamlCfg({
|
||||||
actorOrgId: req.permission.orgId,
|
isActive,
|
||||||
orgId: req.body.organizationId,
|
authProvider,
|
||||||
...req.body
|
issuer,
|
||||||
|
entryPoint,
|
||||||
|
idpCert: cert,
|
||||||
|
actor: permission.type,
|
||||||
|
actorId: permission.id,
|
||||||
|
actorAuthMethod: permission.authMethod,
|
||||||
|
actorOrgId: permission.orgId,
|
||||||
|
orgId: req.body.organizationId
|
||||||
});
|
});
|
||||||
return saml;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import ms from "ms";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { SshCertType } from "@app/ee/services/ssh/ssh-certificate-authority-types";
|
import { SshCertType } from "@app/ee/services/ssh/ssh-certificate-authority-types";
|
||||||
import { SSH_CERTIFICATE_AUTHORITIES } from "@app/lib/api-docs";
|
import { SSH_CERTIFICATE_AUTHORITIES } from "@app/lib/api-docs";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { writeLimit } from "@app/server/config/rateLimiter";
|
import { writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import ms from "ms";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
@@ -10,6 +9,7 @@ import {
|
|||||||
isValidUserPattern
|
isValidUserPattern
|
||||||
} from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-validators";
|
} from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-validators";
|
||||||
import { SSH_CERTIFICATE_TEMPLATES } from "@app/lib/api-docs";
|
import { SSH_CERTIFICATE_TEMPLATES } from "@app/lib/api-docs";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import ms from "ms";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns";
|
import { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns";
|
||||||
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
||||||
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-types";
|
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-types";
|
||||||
import { PROJECT_USER_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
import { PROJECT_USER_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { slugSchema } from "@app/server/lib/schemas";
|
import { slugSchema } from "@app/server/lib/schemas";
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import ms from "ms";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/ee/services/identity-project-additional-privilege-v2/identity-project-additional-privilege-v2-types";
|
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/ee/services/identity-project-additional-privilege-v2/identity-project-additional-privilege-v2-types";
|
||||||
import { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns";
|
import { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns";
|
||||||
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
||||||
import { IDENTITY_ADDITIONAL_PRIVILEGE_V2 } from "@app/lib/api-docs";
|
import { IDENTITY_ADDITIONAL_PRIVILEGE_V2 } from "@app/lib/api-docs";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { slugSchema } from "@app/server/lib/schemas";
|
import { slugSchema } from "@app/server/lib/schemas";
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import ms from "ms";
|
import msFn from "ms";
|
||||||
|
|
||||||
import { ActionProjectType, ProjectMembershipRole } from "@app/db/schemas";
|
import { ActionProjectType, ProjectMembershipRole } from "@app/db/schemas";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
@@ -246,7 +247,7 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
requesterEmail: requestedByUser.email,
|
requesterEmail: requestedByUser.email,
|
||||||
isTemporary,
|
isTemporary,
|
||||||
...(isTemporary && {
|
...(isTemporary && {
|
||||||
expiresIn: ms(ms(temporaryRange || ""), { long: true })
|
expiresIn: msFn(ms(temporaryRange || ""), { long: true })
|
||||||
}),
|
}),
|
||||||
secretPath,
|
secretPath,
|
||||||
environment: envSlug,
|
environment: envSlug,
|
||||||
|
|||||||
@@ -978,6 +978,7 @@ interface AddIdentityOidcAuthEvent {
|
|||||||
boundIssuer: string;
|
boundIssuer: string;
|
||||||
boundAudiences: string;
|
boundAudiences: string;
|
||||||
boundClaims: Record<string, string>;
|
boundClaims: Record<string, string>;
|
||||||
|
claimMetadataMapping: Record<string, string>;
|
||||||
boundSubject: string;
|
boundSubject: string;
|
||||||
accessTokenTTL: number;
|
accessTokenTTL: number;
|
||||||
accessTokenMaxTTL: number;
|
accessTokenMaxTTL: number;
|
||||||
@@ -1002,6 +1003,7 @@ interface UpdateIdentityOidcAuthEvent {
|
|||||||
boundIssuer?: string;
|
boundIssuer?: string;
|
||||||
boundAudiences?: string;
|
boundAudiences?: string;
|
||||||
boundClaims?: Record<string, string>;
|
boundClaims?: Record<string, string>;
|
||||||
|
claimMetadataMapping?: Record<string, string>;
|
||||||
boundSubject?: string;
|
boundSubject?: string;
|
||||||
accessTokenTTL?: number;
|
accessTokenTTL?: number;
|
||||||
accessTokenMaxTTL?: number;
|
accessTokenMaxTTL?: number;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { ForbiddenError, subject } from "@casl/ability";
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
import ms from "ms";
|
|
||||||
|
|
||||||
import { ActionProjectType } from "@app/db/schemas";
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
@@ -11,6 +10,7 @@ import {
|
|||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
|
|||||||
+1
-1
@@ -1,9 +1,9 @@
|
|||||||
import { ForbiddenError, subject } from "@casl/ability";
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
import { packRules } from "@casl/ability/extra";
|
import { packRules } from "@casl/ability/extra";
|
||||||
import ms from "ms";
|
|
||||||
|
|
||||||
import { ActionProjectType, TableName } from "@app/db/schemas";
|
import { ActionProjectType, TableName } from "@app/db/schemas";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { unpackPermissions } from "@app/server/routes/sanitizedSchema/permission";
|
import { unpackPermissions } from "@app/server/routes/sanitizedSchema/permission";
|
||||||
import { ActorType } from "@app/services/auth/auth-type";
|
import { ActorType } from "@app/services/auth/auth-type";
|
||||||
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
||||||
|
|||||||
+1
-1
@@ -1,9 +1,9 @@
|
|||||||
import { ForbiddenError, MongoAbility, RawRuleOf, subject } from "@casl/ability";
|
import { ForbiddenError, MongoAbility, RawRuleOf, subject } from "@casl/ability";
|
||||||
import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
|
import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
|
||||||
import ms from "ms";
|
|
||||||
|
|
||||||
import { ActionProjectType } from "@app/db/schemas";
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { UnpackedPermissionSchema } from "@app/server/routes/sanitizedSchema/permission";
|
import { UnpackedPermissionSchema } from "@app/server/routes/sanitizedSchema/permission";
|
||||||
import { ActorType } from "@app/services/auth/auth-type";
|
import { ActorType } from "@app/services/auth/auth-type";
|
||||||
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import * as x509 from "@peculiar/x509";
|
import * as x509 from "@peculiar/x509";
|
||||||
import crypto, { KeyObject } from "crypto";
|
import crypto, { KeyObject } from "crypto";
|
||||||
import ms from "ms";
|
|
||||||
|
|
||||||
import { ActionProjectType } from "@app/db/schemas";
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { BadRequestError, InternalServerError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, InternalServerError, NotFoundError } from "@app/lib/errors";
|
||||||
import { isValidHostname, isValidIp } from "@app/lib/ip";
|
import { isValidHostname, isValidIp } from "@app/lib/ip";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { constructPemChainFromCerts } from "@app/services/certificate/certificate-fns";
|
import { constructPemChainFromCerts } from "@app/services/certificate/certificate-fns";
|
||||||
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "@app/services/certificate/certificate-types";
|
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "@app/services/certificate/certificate-types";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ export enum OrgPermissionAdminConsoleAction {
|
|||||||
AccessAllProjects = "access-all-projects"
|
AccessAllProjects = "access-all-projects"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum OrgPermissionSecretShareAction {
|
||||||
|
ManageSettings = "manage-settings"
|
||||||
|
}
|
||||||
|
|
||||||
export enum OrgPermissionGatewayActions {
|
export enum OrgPermissionGatewayActions {
|
||||||
// is there a better word for this. This mean can an identity be a gateway
|
// is there a better word for this. This mean can an identity be a gateway
|
||||||
CreateGateways = "create-gateways",
|
CreateGateways = "create-gateways",
|
||||||
@@ -81,7 +85,8 @@ export enum OrgPermissionSubjects {
|
|||||||
ProjectTemplates = "project-templates",
|
ProjectTemplates = "project-templates",
|
||||||
AppConnections = "app-connections",
|
AppConnections = "app-connections",
|
||||||
Kmip = "kmip",
|
Kmip = "kmip",
|
||||||
Gateway = "gateway"
|
Gateway = "gateway",
|
||||||
|
SecretShare = "secret-share"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AppConnectionSubjectFields = {
|
export type AppConnectionSubjectFields = {
|
||||||
@@ -113,7 +118,8 @@ export type OrgPermissionSet =
|
|||||||
)
|
)
|
||||||
]
|
]
|
||||||
| [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole]
|
| [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole]
|
||||||
| [OrgPermissionKmipActions, OrgPermissionSubjects.Kmip];
|
| [OrgPermissionKmipActions, OrgPermissionSubjects.Kmip]
|
||||||
|
| [OrgPermissionSecretShareAction, OrgPermissionSubjects.SecretShare];
|
||||||
|
|
||||||
const AppConnectionConditionSchema = z
|
const AppConnectionConditionSchema = z
|
||||||
.object({
|
.object({
|
||||||
@@ -207,6 +213,12 @@ export const OrgPermissionSchema = z.discriminatedUnion("subject", [
|
|||||||
"Describe what action an entity can take."
|
"Describe what action an entity can take."
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
z.object({
|
||||||
|
subject: z.literal(OrgPermissionSubjects.SecretShare).describe("The entity this permission pertains to."),
|
||||||
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionSecretShareAction).describe(
|
||||||
|
"Describe what action an entity can take."
|
||||||
|
)
|
||||||
|
}),
|
||||||
z.object({
|
z.object({
|
||||||
subject: z.literal(OrgPermissionSubjects.Kmip).describe("The entity this permission pertains to."),
|
subject: z.literal(OrgPermissionSubjects.Kmip).describe("The entity this permission pertains to."),
|
||||||
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionKmipActions).describe(
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionKmipActions).describe(
|
||||||
@@ -322,6 +334,8 @@ const buildAdminPermission = () => {
|
|||||||
// the proxy assignment is temporary in order to prevent "more privilege" error during role assignment to MI
|
// the proxy assignment is temporary in order to prevent "more privilege" error during role assignment to MI
|
||||||
can(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
can(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
||||||
|
|
||||||
|
can(OrgPermissionSecretShareAction.ManageSettings, OrgPermissionSubjects.SecretShare);
|
||||||
|
|
||||||
return rules;
|
return rules;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -133,12 +133,12 @@ function validateOrgSSO(actorAuthMethod: ActorAuthMethod, isOrgSsoEnforced: TOrg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const escapeHandlebarsMissingMetadata = (obj: Record<string, string>) => {
|
const escapeHandlebarsMissingDict = (obj: Record<string, string>, key: string) => {
|
||||||
const handler = {
|
const handler = {
|
||||||
get(target: Record<string, string>, prop: string) {
|
get(target: Record<string, string>, prop: string) {
|
||||||
if (!(prop in target)) {
|
if (!Object.hasOwn(target, prop)) {
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
target[prop] = `{{identity.metadata.${prop}}}`; // Add missing key as an "own" property
|
target[prop] = `{{${key}.${prop}}}`; // Add missing key as an "own" property
|
||||||
}
|
}
|
||||||
return target[prop];
|
return target[prop];
|
||||||
}
|
}
|
||||||
@@ -194,7 +194,7 @@ const constructPermissionErrorMessage = (
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
constructPermissionErrorMessage,
|
constructPermissionErrorMessage,
|
||||||
escapeHandlebarsMissingMetadata,
|
escapeHandlebarsMissingDict,
|
||||||
isAuthMethodSaml,
|
isAuthMethodSaml,
|
||||||
validateOrgSSO,
|
validateOrgSSO,
|
||||||
validatePrivilegeChangeOperation
|
validatePrivilegeChangeOperation
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { createMongoAbility, MongoAbility, RawRuleOf } from "@casl/ability";
|
import { createMongoAbility, MongoAbility, RawRuleOf } from "@casl/ability";
|
||||||
import { PackRule, unpackRules } from "@casl/ability/extra";
|
import { PackRule, unpackRules } from "@casl/ability/extra";
|
||||||
|
import { requestContext } from "@fastify/request-context";
|
||||||
import { MongoQuery } from "@ucast/mongo2js";
|
import { MongoQuery } from "@ucast/mongo2js";
|
||||||
import handlebars from "handlebars";
|
import handlebars from "handlebars";
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ import { TServiceTokenDALFactory } from "@app/services/service-token/service-tok
|
|||||||
|
|
||||||
import { orgAdminPermissions, orgMemberPermissions, orgNoAccessPermissions, OrgPermissionSet } from "./org-permission";
|
import { orgAdminPermissions, orgMemberPermissions, orgNoAccessPermissions, OrgPermissionSet } from "./org-permission";
|
||||||
import { TPermissionDALFactory } from "./permission-dal";
|
import { TPermissionDALFactory } from "./permission-dal";
|
||||||
import { escapeHandlebarsMissingMetadata, validateOrgSSO } from "./permission-fns";
|
import { escapeHandlebarsMissingDict, validateOrgSSO } from "./permission-fns";
|
||||||
import {
|
import {
|
||||||
TBuildOrgPermissionDTO,
|
TBuildOrgPermissionDTO,
|
||||||
TBuildProjectPermissionDTO,
|
TBuildProjectPermissionDTO,
|
||||||
@@ -243,20 +244,22 @@ export const permissionServiceFactory = ({
|
|||||||
|
|
||||||
const rules = buildProjectPermissionRules(rolePermissions.concat(additionalPrivileges));
|
const rules = buildProjectPermissionRules(rolePermissions.concat(additionalPrivileges));
|
||||||
const templatedRules = handlebars.compile(JSON.stringify(rules), { data: false });
|
const templatedRules = handlebars.compile(JSON.stringify(rules), { data: false });
|
||||||
const metadataKeyValuePair = escapeHandlebarsMissingMetadata(
|
const metadataKeyValuePair = escapeHandlebarsMissingDict(
|
||||||
objectify(
|
objectify(
|
||||||
userProjectPermission.metadata,
|
userProjectPermission.metadata,
|
||||||
(i) => i.key,
|
(i) => i.key,
|
||||||
(i) => i.value
|
(i) => i.value
|
||||||
)
|
),
|
||||||
|
"identity.metadata"
|
||||||
);
|
);
|
||||||
|
const templateValue = {
|
||||||
|
id: userProjectPermission.userId,
|
||||||
|
username: userProjectPermission.username,
|
||||||
|
metadata: metadataKeyValuePair
|
||||||
|
};
|
||||||
const interpolateRules = templatedRules(
|
const interpolateRules = templatedRules(
|
||||||
{
|
{
|
||||||
identity: {
|
identity: templateValue
|
||||||
id: userProjectPermission.userId,
|
|
||||||
username: userProjectPermission.username,
|
|
||||||
metadata: metadataKeyValuePair
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{ data: false }
|
{ data: false }
|
||||||
);
|
);
|
||||||
@@ -317,21 +320,26 @@ export const permissionServiceFactory = ({
|
|||||||
|
|
||||||
const rules = buildProjectPermissionRules(rolePermissions.concat(additionalPrivileges));
|
const rules = buildProjectPermissionRules(rolePermissions.concat(additionalPrivileges));
|
||||||
const templatedRules = handlebars.compile(JSON.stringify(rules), { data: false });
|
const templatedRules = handlebars.compile(JSON.stringify(rules), { data: false });
|
||||||
const metadataKeyValuePair = escapeHandlebarsMissingMetadata(
|
const unescapedIdentityAuthInfo = requestContext.get("identityAuthInfo");
|
||||||
objectify(
|
const unescapedMetadata = objectify(
|
||||||
identityProjectPermission.metadata,
|
identityProjectPermission.metadata,
|
||||||
(i) => i.key,
|
(i) => i.key,
|
||||||
(i) => i.value
|
(i) => i.value
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
const identityAuthInfo =
|
||||||
|
unescapedIdentityAuthInfo?.identityId === identityId && unescapedIdentityAuthInfo
|
||||||
|
? escapeHandlebarsMissingDict(unescapedIdentityAuthInfo as never, "identity.auth")
|
||||||
|
: {};
|
||||||
|
const metadataKeyValuePair = escapeHandlebarsMissingDict(unescapedMetadata, "identity.metadata");
|
||||||
|
const templateValue = {
|
||||||
|
id: identityProjectPermission.identityId,
|
||||||
|
username: identityProjectPermission.username,
|
||||||
|
metadata: metadataKeyValuePair,
|
||||||
|
auth: identityAuthInfo
|
||||||
|
};
|
||||||
const interpolateRules = templatedRules(
|
const interpolateRules = templatedRules(
|
||||||
{
|
{
|
||||||
identity: {
|
identity: templateValue
|
||||||
id: identityProjectPermission.identityId,
|
|
||||||
username: identityProjectPermission.username,
|
|
||||||
metadata: metadataKeyValuePair
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{ data: false }
|
{ data: false }
|
||||||
);
|
);
|
||||||
@@ -429,20 +437,22 @@ export const permissionServiceFactory = ({
|
|||||||
|
|
||||||
const rules = buildProjectPermissionRules(rolePermissions.concat(additionalPrivileges));
|
const rules = buildProjectPermissionRules(rolePermissions.concat(additionalPrivileges));
|
||||||
const templatedRules = handlebars.compile(JSON.stringify(rules), { data: false });
|
const templatedRules = handlebars.compile(JSON.stringify(rules), { data: false });
|
||||||
const metadataKeyValuePair = escapeHandlebarsMissingMetadata(
|
const metadataKeyValuePair = escapeHandlebarsMissingDict(
|
||||||
objectify(
|
objectify(
|
||||||
userProjectPermission.metadata,
|
userProjectPermission.metadata,
|
||||||
(i) => i.key,
|
(i) => i.key,
|
||||||
(i) => i.value
|
(i) => i.value
|
||||||
)
|
),
|
||||||
|
"identity.metadata"
|
||||||
);
|
);
|
||||||
|
const templateValue = {
|
||||||
|
id: userProjectPermission.userId,
|
||||||
|
username: userProjectPermission.username,
|
||||||
|
metadata: metadataKeyValuePair
|
||||||
|
};
|
||||||
const interpolateRules = templatedRules(
|
const interpolateRules = templatedRules(
|
||||||
{
|
{
|
||||||
identity: {
|
identity: templateValue
|
||||||
id: userProjectPermission.userId,
|
|
||||||
username: userProjectPermission.username,
|
|
||||||
metadata: metadataKeyValuePair
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{ data: false }
|
{ data: false }
|
||||||
);
|
);
|
||||||
@@ -474,21 +484,22 @@ export const permissionServiceFactory = ({
|
|||||||
|
|
||||||
const rules = buildProjectPermissionRules(rolePermissions.concat(additionalPrivileges));
|
const rules = buildProjectPermissionRules(rolePermissions.concat(additionalPrivileges));
|
||||||
const templatedRules = handlebars.compile(JSON.stringify(rules), { data: false });
|
const templatedRules = handlebars.compile(JSON.stringify(rules), { data: false });
|
||||||
const metadataKeyValuePair = escapeHandlebarsMissingMetadata(
|
const metadataKeyValuePair = escapeHandlebarsMissingDict(
|
||||||
objectify(
|
objectify(
|
||||||
identityProjectPermission.metadata,
|
identityProjectPermission.metadata,
|
||||||
(i) => i.key,
|
(i) => i.key,
|
||||||
(i) => i.value
|
(i) => i.value
|
||||||
)
|
),
|
||||||
|
"identity.metadata"
|
||||||
);
|
);
|
||||||
|
const templateValue = {
|
||||||
|
id: identityProjectPermission.identityId,
|
||||||
|
username: identityProjectPermission.username,
|
||||||
|
metadata: metadataKeyValuePair
|
||||||
|
};
|
||||||
const interpolateRules = templatedRules(
|
const interpolateRules = templatedRules(
|
||||||
{
|
{
|
||||||
identity: {
|
identity: templateValue
|
||||||
id: identityProjectPermission.identityId,
|
|
||||||
username: identityProjectPermission.username,
|
|
||||||
metadata: metadataKeyValuePair
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{ data: false }
|
{ data: false }
|
||||||
);
|
);
|
||||||
|
|||||||
+1
-1
@@ -1,9 +1,9 @@
|
|||||||
import { ForbiddenError, MongoAbility, RawRuleOf } from "@casl/ability";
|
import { ForbiddenError, MongoAbility, RawRuleOf } from "@casl/ability";
|
||||||
import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
|
import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
|
||||||
import ms from "ms";
|
|
||||||
|
|
||||||
import { ActionProjectType, TableName } from "@app/db/schemas";
|
import { ActionProjectType, TableName } from "@app/db/schemas";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { UnpackedPermissionSchema } from "@app/server/routes/sanitizedSchema/permission";
|
import { UnpackedPermissionSchema } from "@app/server/routes/sanitizedSchema/permission";
|
||||||
import { ActorType } from "@app/services/auth/auth-type";
|
import { ActorType } from "@app/services/auth/auth-type";
|
||||||
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export const samlConfigServiceFactory = ({
|
|||||||
kmsService
|
kmsService
|
||||||
}: TSamlConfigServiceFactoryDep) => {
|
}: TSamlConfigServiceFactoryDep) => {
|
||||||
const createSamlCfg = async ({
|
const createSamlCfg = async ({
|
||||||
cert,
|
idpCert,
|
||||||
actor,
|
actor,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId,
|
actorOrgId,
|
||||||
@@ -93,9 +93,9 @@ export const samlConfigServiceFactory = ({
|
|||||||
orgId,
|
orgId,
|
||||||
authProvider,
|
authProvider,
|
||||||
isActive,
|
isActive,
|
||||||
encryptedSamlIssuer: encryptor({ plainText: Buffer.from(issuer) }).cipherTextBlob,
|
encryptedSamlCertificate: encryptor({ plainText: Buffer.from(idpCert) }).cipherTextBlob,
|
||||||
encryptedSamlEntryPoint: encryptor({ plainText: Buffer.from(entryPoint) }).cipherTextBlob,
|
encryptedSamlEntryPoint: encryptor({ plainText: Buffer.from(entryPoint) }).cipherTextBlob,
|
||||||
encryptedSamlCertificate: encryptor({ plainText: Buffer.from(cert) }).cipherTextBlob
|
encryptedSamlIssuer: encryptor({ plainText: Buffer.from(issuer) }).cipherTextBlob
|
||||||
});
|
});
|
||||||
|
|
||||||
return samlConfig;
|
return samlConfig;
|
||||||
@@ -106,7 +106,7 @@ export const samlConfigServiceFactory = ({
|
|||||||
actor,
|
actor,
|
||||||
actorOrgId,
|
actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
cert,
|
idpCert,
|
||||||
actorId,
|
actorId,
|
||||||
issuer,
|
issuer,
|
||||||
isActive,
|
isActive,
|
||||||
@@ -136,8 +136,8 @@ export const samlConfigServiceFactory = ({
|
|||||||
updateQuery.encryptedSamlIssuer = encryptor({ plainText: Buffer.from(issuer) }).cipherTextBlob;
|
updateQuery.encryptedSamlIssuer = encryptor({ plainText: Buffer.from(issuer) }).cipherTextBlob;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cert !== undefined) {
|
if (idpCert !== undefined) {
|
||||||
updateQuery.encryptedSamlCertificate = encryptor({ plainText: Buffer.from(cert) }).cipherTextBlob;
|
updateQuery.encryptedSamlCertificate = encryptor({ plainText: Buffer.from(idpCert) }).cipherTextBlob;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [ssoConfig] = await samlConfigDAL.update({ orgId }, updateQuery);
|
const [ssoConfig] = await samlConfigDAL.update({ orgId }, updateQuery);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export type TCreateSamlCfgDTO = {
|
|||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
entryPoint: string;
|
entryPoint: string;
|
||||||
issuer: string;
|
issuer: string;
|
||||||
cert: string;
|
idpCert: string;
|
||||||
} & TOrgPermission;
|
} & TOrgPermission;
|
||||||
|
|
||||||
export type TUpdateSamlCfgDTO = Partial<{
|
export type TUpdateSamlCfgDTO = Partial<{
|
||||||
@@ -23,7 +23,7 @@ export type TUpdateSamlCfgDTO = Partial<{
|
|||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
entryPoint: string;
|
entryPoint: string;
|
||||||
issuer: string;
|
issuer: string;
|
||||||
cert: string;
|
idpCert: string;
|
||||||
}> &
|
}> &
|
||||||
TOrgPermission;
|
TOrgPermission;
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,10 +1,10 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import ms from "ms";
|
|
||||||
|
|
||||||
import { ActionProjectType } from "@app/db/schemas";
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
|
|
||||||
import { TSshCertificateAuthorityDALFactory } from "../ssh/ssh-certificate-authority-dal";
|
import { TSshCertificateAuthorityDALFactory } from "../ssh/ssh-certificate-authority-dal";
|
||||||
import { TSshCertificateTemplateDALFactory } from "./ssh-certificate-template-dal";
|
import { TSshCertificateTemplateDALFactory } from "./ssh-certificate-template-dal";
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { execFile } from "child_process";
|
import { execFile } from "child_process";
|
||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
import { promises as fs } from "fs";
|
import { promises as fs } from "fs";
|
||||||
import ms from "ms";
|
|
||||||
import os from "os";
|
import os from "os";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { promisify } from "util";
|
import { promisify } from "util";
|
||||||
|
|
||||||
import { TSshCertificateTemplates } from "@app/db/schemas";
|
import { TSshCertificateTemplates } from "@app/db/schemas";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { CertKeyAlgorithm } from "@app/services/certificate/certificate-types";
|
import { CertKeyAlgorithm } from "@app/services/certificate/certificate-types";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -329,6 +329,7 @@ export const OIDC_AUTH = {
|
|||||||
boundIssuer: "The unique identifier of the identity provider issuing the JWT.",
|
boundIssuer: "The unique identifier of the identity provider issuing the JWT.",
|
||||||
boundAudiences: "The list of intended recipients.",
|
boundAudiences: "The list of intended recipients.",
|
||||||
boundClaims: "The attributes that should be present in the JWT for it to be valid.",
|
boundClaims: "The attributes that should be present in the JWT for it to be valid.",
|
||||||
|
claimMetadataMapping: "The attributes that should be present in the permission metadata from the JWT.",
|
||||||
boundSubject: "The expected principal that is the subject of the JWT.",
|
boundSubject: "The expected principal that is the subject of the JWT.",
|
||||||
accessTokenTrustedIps: "The IPs or CIDR ranges that access tokens can be used from.",
|
accessTokenTrustedIps: "The IPs or CIDR ranges that access tokens can be used from.",
|
||||||
accessTokenTTL: "The lifetime for an access token in seconds.",
|
accessTokenTTL: "The lifetime for an access token in seconds.",
|
||||||
@@ -342,6 +343,7 @@ export const OIDC_AUTH = {
|
|||||||
boundIssuer: "The new unique identifier of the identity provider issuing the JWT.",
|
boundIssuer: "The new unique identifier of the identity provider issuing the JWT.",
|
||||||
boundAudiences: "The new list of intended recipients.",
|
boundAudiences: "The new list of intended recipients.",
|
||||||
boundClaims: "The new attributes that should be present in the JWT for it to be valid.",
|
boundClaims: "The new attributes that should be present in the JWT for it to be valid.",
|
||||||
|
claimMetadataMapping: "The new attributes that should be present in the permission metadata from the JWT.",
|
||||||
boundSubject: "The new expected principal that is the subject of the JWT.",
|
boundSubject: "The new expected principal that is the subject of the JWT.",
|
||||||
accessTokenTrustedIps: "The new IPs or CIDR ranges that access tokens can be used from.",
|
accessTokenTrustedIps: "The new IPs or CIDR ranges that access tokens can be used from.",
|
||||||
accessTokenTTL: "The new lifetime for an access token in seconds.",
|
accessTokenTTL: "The new lifetime for an access token in seconds.",
|
||||||
@@ -1725,7 +1727,8 @@ export const SecretSyncs = {
|
|||||||
SYNC_OPTIONS: (destination: SecretSync) => {
|
SYNC_OPTIONS: (destination: SecretSync) => {
|
||||||
const destinationName = SECRET_SYNC_NAME_MAP[destination];
|
const destinationName = SECRET_SYNC_NAME_MAP[destination];
|
||||||
return {
|
return {
|
||||||
initialSyncBehavior: `Specify how Infisical should resolve the initial sync to the ${destinationName} destination.`
|
initialSyncBehavior: `Specify how Infisical should resolve the initial sync to the ${destinationName} destination.`,
|
||||||
|
disableSecretDeletion: `Enable this flag to prevent removal of secrets from the ${destinationName} destination when syncing.`
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
ADDITIONAL_SYNC_OPTIONS: {
|
ADDITIONAL_SYNC_OPTIONS: {
|
||||||
@@ -1771,6 +1774,12 @@ export const SecretSyncs = {
|
|||||||
},
|
},
|
||||||
DATABRICKS: {
|
DATABRICKS: {
|
||||||
scope: "The Databricks secret scope that secrets should be synced to."
|
scope: "The Databricks secret scope that secrets should be synced to."
|
||||||
|
},
|
||||||
|
HUMANITEC: {
|
||||||
|
app: "The ID of the Humanitec app to sync secrets to.",
|
||||||
|
org: "The ID of the Humanitec org to sync secrets to.",
|
||||||
|
env: "The ID of the Humanitec environment to sync secrets to.",
|
||||||
|
scope: "The Humanitec scope that secrets should be synced to."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import msFn, { StringValue } from "ms";
|
||||||
|
|
||||||
|
import { BadRequestError } from "../errors";
|
||||||
|
|
||||||
|
export const ms = (val: string) => {
|
||||||
|
if (typeof val !== "string") {
|
||||||
|
throw new BadRequestError({ message: `Date must be string` });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return msFn(val as StringValue);
|
||||||
|
} catch {
|
||||||
|
throw new BadRequestError({ message: `Invalid date format string: ${val}` });
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* Safely retrieves a value from a nested object using dot notation path
|
||||||
|
*/
|
||||||
|
export const getStringValueByDot = (
|
||||||
|
obj: Record<string, unknown> | null | undefined,
|
||||||
|
path: string,
|
||||||
|
defaultValue?: string
|
||||||
|
): string | undefined => {
|
||||||
|
// Handle null or undefined input
|
||||||
|
if (!obj) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parts = path.split(".");
|
||||||
|
let current: unknown = obj;
|
||||||
|
|
||||||
|
for (const part of parts) {
|
||||||
|
const isObject = typeof current === "object" && !Array.isArray(current) && current !== null;
|
||||||
|
if (!isObject) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
if (!Object.hasOwn(current as object, part)) {
|
||||||
|
// Check if the property exists as an own property
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
current = (current as Record<string, unknown>)[part];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof current !== "string") {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return current;
|
||||||
|
};
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { requestContext } from "@fastify/request-context";
|
||||||
import { FastifyRequest } from "fastify";
|
import { FastifyRequest } from "fastify";
|
||||||
import fp from "fastify-plugin";
|
import fp from "fastify-plugin";
|
||||||
import jwt, { JwtPayload } from "jsonwebtoken";
|
import jwt, { JwtPayload } from "jsonwebtoken";
|
||||||
@@ -137,6 +138,12 @@ export const injectIdentity = fp(async (server: FastifyZodProvider) => {
|
|||||||
identityName: identity.name,
|
identityName: identity.name,
|
||||||
authMethod: null
|
authMethod: null
|
||||||
};
|
};
|
||||||
|
if (token?.identityAuth?.oidc) {
|
||||||
|
requestContext.set("identityAuthInfo", {
|
||||||
|
identityId: identity.identityId,
|
||||||
|
oidc: token?.identityAuth?.oidc
|
||||||
|
});
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AuthMode.SERVICE_TOKEN: {
|
case AuthMode.SERVICE_TOKEN: {
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ import {
|
|||||||
} from "@app/services/app-connection/databricks";
|
} from "@app/services/app-connection/databricks";
|
||||||
import { GcpConnectionListItemSchema, SanitizedGcpConnectionSchema } from "@app/services/app-connection/gcp";
|
import { GcpConnectionListItemSchema, SanitizedGcpConnectionSchema } from "@app/services/app-connection/gcp";
|
||||||
import { GitHubConnectionListItemSchema, SanitizedGitHubConnectionSchema } from "@app/services/app-connection/github";
|
import { GitHubConnectionListItemSchema, SanitizedGitHubConnectionSchema } from "@app/services/app-connection/github";
|
||||||
|
import {
|
||||||
|
HumanitecConnectionListItemSchema,
|
||||||
|
SanitizedHumanitecConnectionSchema
|
||||||
|
} from "@app/services/app-connection/humanitec";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
// can't use discriminated due to multiple schemas for certain apps
|
// can't use discriminated due to multiple schemas for certain apps
|
||||||
@@ -27,7 +31,8 @@ const SanitizedAppConnectionSchema = z.union([
|
|||||||
...SanitizedGcpConnectionSchema.options,
|
...SanitizedGcpConnectionSchema.options,
|
||||||
...SanitizedAzureKeyVaultConnectionSchema.options,
|
...SanitizedAzureKeyVaultConnectionSchema.options,
|
||||||
...SanitizedAzureAppConfigurationConnectionSchema.options,
|
...SanitizedAzureAppConfigurationConnectionSchema.options,
|
||||||
...SanitizedDatabricksConnectionSchema.options
|
...SanitizedDatabricksConnectionSchema.options,
|
||||||
|
...SanitizedHumanitecConnectionSchema.options
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
|
const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
|
||||||
@@ -36,7 +41,8 @@ const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
|
|||||||
GcpConnectionListItemSchema,
|
GcpConnectionListItemSchema,
|
||||||
AzureKeyVaultConnectionListItemSchema,
|
AzureKeyVaultConnectionListItemSchema,
|
||||||
AzureAppConfigurationConnectionListItemSchema,
|
AzureAppConfigurationConnectionListItemSchema,
|
||||||
DatabricksConnectionListItemSchema
|
DatabricksConnectionListItemSchema,
|
||||||
|
HumanitecConnectionListItemSchema
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const registerAppConnectionRouter = async (server: FastifyZodProvider) => {
|
export const registerAppConnectionRouter = async (server: FastifyZodProvider) => {
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import z from "zod";
|
||||||
|
|
||||||
|
import { readLimit } from "@app/server/config/rateLimiter";
|
||||||
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||||
|
import {
|
||||||
|
CreateHumanitecConnectionSchema,
|
||||||
|
HumanitecOrgWithApps,
|
||||||
|
SanitizedHumanitecConnectionSchema,
|
||||||
|
UpdateHumanitecConnectionSchema
|
||||||
|
} from "@app/services/app-connection/humanitec";
|
||||||
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
import { registerAppConnectionEndpoints } from "./app-connection-endpoints";
|
||||||
|
|
||||||
|
export const registerHumanitecConnectionRouter = async (server: FastifyZodProvider) => {
|
||||||
|
registerAppConnectionEndpoints({
|
||||||
|
app: AppConnection.Humanitec,
|
||||||
|
server,
|
||||||
|
sanitizedResponseSchema: SanitizedHumanitecConnectionSchema,
|
||||||
|
createSchema: CreateHumanitecConnectionSchema,
|
||||||
|
updateSchema: UpdateHumanitecConnectionSchema
|
||||||
|
});
|
||||||
|
|
||||||
|
// The below endpoints are not exposed and for Infisical App use
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: `/:connectionId/organizations`,
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
connectionId: z.string().uuid()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z
|
||||||
|
.object({
|
||||||
|
id: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
apps: z
|
||||||
|
.object({
|
||||||
|
id: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
envs: z
|
||||||
|
.object({
|
||||||
|
id: z.string(),
|
||||||
|
name: z.string()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { connectionId } = req.params;
|
||||||
|
|
||||||
|
const organizations: HumanitecOrgWithApps[] = await server.services.appConnection.humanitec.listOrganizations(
|
||||||
|
connectionId,
|
||||||
|
req.permission
|
||||||
|
);
|
||||||
|
|
||||||
|
return organizations;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -6,6 +6,7 @@ import { registerAzureKeyVaultConnectionRouter } from "./azure-key-vault-connect
|
|||||||
import { registerDatabricksConnectionRouter } from "./databricks-connection-router";
|
import { registerDatabricksConnectionRouter } from "./databricks-connection-router";
|
||||||
import { registerGcpConnectionRouter } from "./gcp-connection-router";
|
import { registerGcpConnectionRouter } from "./gcp-connection-router";
|
||||||
import { registerGitHubConnectionRouter } from "./github-connection-router";
|
import { registerGitHubConnectionRouter } from "./github-connection-router";
|
||||||
|
import { registerHumanitecConnectionRouter } from "./humanitec-connection-router";
|
||||||
|
|
||||||
export * from "./app-connection-router";
|
export * from "./app-connection-router";
|
||||||
|
|
||||||
@@ -16,5 +17,6 @@ export const APP_CONNECTION_REGISTER_ROUTER_MAP: Record<AppConnection, (server:
|
|||||||
[AppConnection.GCP]: registerGcpConnectionRouter,
|
[AppConnection.GCP]: registerGcpConnectionRouter,
|
||||||
[AppConnection.AzureKeyVault]: registerAzureKeyVaultConnectionRouter,
|
[AppConnection.AzureKeyVault]: registerAzureKeyVaultConnectionRouter,
|
||||||
[AppConnection.AzureAppConfiguration]: registerAzureAppConfigurationConnectionRouter,
|
[AppConnection.AzureAppConfiguration]: registerAzureAppConfigurationConnectionRouter,
|
||||||
[AppConnection.Databricks]: registerDatabricksConnectionRouter
|
[AppConnection.Databricks]: registerDatabricksConnectionRouter,
|
||||||
|
[AppConnection.Humanitec]: registerHumanitecConnectionRouter
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-floating-promises */
|
/* eslint-disable @typescript-eslint/no-floating-promises */
|
||||||
import ms from "ms";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { CertificateAuthoritiesSchema, CertificateTemplatesSchema } from "@app/db/schemas";
|
import { CertificateAuthoritiesSchema, CertificateTemplatesSchema } from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { CERTIFICATE_AUTHORITIES } from "@app/lib/api-docs";
|
import { CERTIFICATE_AUTHORITIES } from "@app/lib/api-docs";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import ms from "ms";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { CertificatesSchema } from "@app/db/schemas";
|
import { CertificatesSchema } from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { CERTIFICATE_AUTHORITIES, CERTIFICATES } from "@app/lib/api-docs";
|
import { CERTIFICATE_AUTHORITIES, CERTIFICATES } from "@app/lib/api-docs";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import ms from "ms";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { CertificateTemplateEstConfigsSchema } from "@app/db/schemas";
|
import { CertificateTemplateEstConfigsSchema } from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { CERTIFICATE_TEMPLATES } from "@app/lib/api-docs";
|
import { CERTIFICATE_TEMPLATES } from "@app/lib/api-docs";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ const IdentityOidcAuthResponseSchema = IdentityOidcAuthsSchema.pick({
|
|||||||
boundIssuer: true,
|
boundIssuer: true,
|
||||||
boundAudiences: true,
|
boundAudiences: true,
|
||||||
boundClaims: true,
|
boundClaims: true,
|
||||||
|
claimMetadataMapping: true,
|
||||||
boundSubject: true,
|
boundSubject: true,
|
||||||
createdAt: true,
|
createdAt: true,
|
||||||
updatedAt: true
|
updatedAt: true
|
||||||
@@ -104,6 +105,7 @@ export const registerIdentityOidcAuthRouter = async (server: FastifyZodProvider)
|
|||||||
boundIssuer: z.string().min(1).describe(OIDC_AUTH.ATTACH.boundIssuer),
|
boundIssuer: z.string().min(1).describe(OIDC_AUTH.ATTACH.boundIssuer),
|
||||||
boundAudiences: validateOidcAuthAudiencesField.describe(OIDC_AUTH.ATTACH.boundAudiences),
|
boundAudiences: validateOidcAuthAudiencesField.describe(OIDC_AUTH.ATTACH.boundAudiences),
|
||||||
boundClaims: validateOidcBoundClaimsField.describe(OIDC_AUTH.ATTACH.boundClaims),
|
boundClaims: validateOidcBoundClaimsField.describe(OIDC_AUTH.ATTACH.boundClaims),
|
||||||
|
claimMetadataMapping: validateOidcBoundClaimsField.describe(OIDC_AUTH.ATTACH.claimMetadataMapping).optional(),
|
||||||
boundSubject: z.string().optional().default("").describe(OIDC_AUTH.ATTACH.boundSubject),
|
boundSubject: z.string().optional().default("").describe(OIDC_AUTH.ATTACH.boundSubject),
|
||||||
accessTokenTrustedIps: z
|
accessTokenTrustedIps: z
|
||||||
.object({
|
.object({
|
||||||
@@ -161,6 +163,7 @@ export const registerIdentityOidcAuthRouter = async (server: FastifyZodProvider)
|
|||||||
boundIssuer: identityOidcAuth.boundIssuer,
|
boundIssuer: identityOidcAuth.boundIssuer,
|
||||||
boundAudiences: identityOidcAuth.boundAudiences,
|
boundAudiences: identityOidcAuth.boundAudiences,
|
||||||
boundClaims: identityOidcAuth.boundClaims as Record<string, string>,
|
boundClaims: identityOidcAuth.boundClaims as Record<string, string>,
|
||||||
|
claimMetadataMapping: identityOidcAuth.claimMetadataMapping as Record<string, string>,
|
||||||
boundSubject: identityOidcAuth.boundSubject as string,
|
boundSubject: identityOidcAuth.boundSubject as string,
|
||||||
accessTokenTTL: identityOidcAuth.accessTokenTTL,
|
accessTokenTTL: identityOidcAuth.accessTokenTTL,
|
||||||
accessTokenMaxTTL: identityOidcAuth.accessTokenMaxTTL,
|
accessTokenMaxTTL: identityOidcAuth.accessTokenMaxTTL,
|
||||||
@@ -200,6 +203,7 @@ export const registerIdentityOidcAuthRouter = async (server: FastifyZodProvider)
|
|||||||
boundIssuer: z.string().min(1).describe(OIDC_AUTH.UPDATE.boundIssuer),
|
boundIssuer: z.string().min(1).describe(OIDC_AUTH.UPDATE.boundIssuer),
|
||||||
boundAudiences: validateOidcAuthAudiencesField.describe(OIDC_AUTH.UPDATE.boundAudiences),
|
boundAudiences: validateOidcAuthAudiencesField.describe(OIDC_AUTH.UPDATE.boundAudiences),
|
||||||
boundClaims: validateOidcBoundClaimsField.describe(OIDC_AUTH.UPDATE.boundClaims),
|
boundClaims: validateOidcBoundClaimsField.describe(OIDC_AUTH.UPDATE.boundClaims),
|
||||||
|
claimMetadataMapping: validateOidcBoundClaimsField.describe(OIDC_AUTH.UPDATE.claimMetadataMapping).optional(),
|
||||||
boundSubject: z.string().optional().default("").describe(OIDC_AUTH.UPDATE.boundSubject),
|
boundSubject: z.string().optional().default("").describe(OIDC_AUTH.UPDATE.boundSubject),
|
||||||
accessTokenTrustedIps: z
|
accessTokenTrustedIps: z
|
||||||
.object({
|
.object({
|
||||||
@@ -258,6 +262,7 @@ export const registerIdentityOidcAuthRouter = async (server: FastifyZodProvider)
|
|||||||
boundIssuer: identityOidcAuth.boundIssuer,
|
boundIssuer: identityOidcAuth.boundIssuer,
|
||||||
boundAudiences: identityOidcAuth.boundAudiences,
|
boundAudiences: identityOidcAuth.boundAudiences,
|
||||||
boundClaims: identityOidcAuth.boundClaims as Record<string, string>,
|
boundClaims: identityOidcAuth.boundClaims as Record<string, string>,
|
||||||
|
claimMetadataMapping: identityOidcAuth.claimMetadataMapping as Record<string, string>,
|
||||||
boundSubject: identityOidcAuth.boundSubject as string,
|
boundSubject: identityOidcAuth.boundSubject as string,
|
||||||
accessTokenTTL: identityOidcAuth.accessTokenTTL,
|
accessTokenTTL: identityOidcAuth.accessTokenTTL,
|
||||||
accessTokenMaxTTL: identityOidcAuth.accessTokenMaxTTL,
|
accessTokenMaxTTL: identityOidcAuth.accessTokenMaxTTL,
|
||||||
|
|||||||
@@ -256,7 +256,8 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
|||||||
scimEnabled: z.boolean().optional(),
|
scimEnabled: z.boolean().optional(),
|
||||||
defaultMembershipRoleSlug: slugSchema({ max: 64, field: "Default Membership Role" }).optional(),
|
defaultMembershipRoleSlug: slugSchema({ max: 64, field: "Default Membership Role" }).optional(),
|
||||||
enforceMfa: z.boolean().optional(),
|
enforceMfa: z.boolean().optional(),
|
||||||
selectedMfaMethod: z.nativeEnum(MfaMethod).optional()
|
selectedMfaMethod: z.nativeEnum(MfaMethod).optional(),
|
||||||
|
allowSecretSharingOutsideOrganization: z.boolean().optional()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import ms from "ms";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -10,6 +9,7 @@ import {
|
|||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { PROJECT_USERS } from "@app/lib/api-docs";
|
import { PROJECT_USERS } from "@app/lib/api-docs";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import {
|
||||||
|
CreateHumanitecSyncSchema,
|
||||||
|
HumanitecSyncSchema,
|
||||||
|
UpdateHumanitecSyncSchema
|
||||||
|
} from "@app/services/secret-sync/humanitec";
|
||||||
|
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||||
|
|
||||||
|
import { registerSyncSecretsEndpoints } from "./secret-sync-endpoints";
|
||||||
|
|
||||||
|
export const registerHumanitecSyncRouter = async (server: FastifyZodProvider) =>
|
||||||
|
registerSyncSecretsEndpoints({
|
||||||
|
destination: SecretSync.Humanitec,
|
||||||
|
server,
|
||||||
|
responseSchema: HumanitecSyncSchema,
|
||||||
|
createSchema: CreateHumanitecSyncSchema,
|
||||||
|
updateSchema: UpdateHumanitecSyncSchema
|
||||||
|
});
|
||||||
@@ -7,6 +7,7 @@ import { registerAzureKeyVaultSyncRouter } from "./azure-key-vault-sync-router";
|
|||||||
import { registerDatabricksSyncRouter } from "./databricks-sync-router";
|
import { registerDatabricksSyncRouter } from "./databricks-sync-router";
|
||||||
import { registerGcpSyncRouter } from "./gcp-sync-router";
|
import { registerGcpSyncRouter } from "./gcp-sync-router";
|
||||||
import { registerGitHubSyncRouter } from "./github-sync-router";
|
import { registerGitHubSyncRouter } from "./github-sync-router";
|
||||||
|
import { registerHumanitecSyncRouter } from "./humanitec-sync-router";
|
||||||
|
|
||||||
export * from "./secret-sync-router";
|
export * from "./secret-sync-router";
|
||||||
|
|
||||||
@@ -17,5 +18,6 @@ export const SECRET_SYNC_REGISTER_ROUTER_MAP: Record<SecretSync, (server: Fastif
|
|||||||
[SecretSync.GCPSecretManager]: registerGcpSyncRouter,
|
[SecretSync.GCPSecretManager]: registerGcpSyncRouter,
|
||||||
[SecretSync.AzureKeyVault]: registerAzureKeyVaultSyncRouter,
|
[SecretSync.AzureKeyVault]: registerAzureKeyVaultSyncRouter,
|
||||||
[SecretSync.AzureAppConfiguration]: registerAzureAppConfigurationSyncRouter,
|
[SecretSync.AzureAppConfiguration]: registerAzureAppConfigurationSyncRouter,
|
||||||
[SecretSync.Databricks]: registerDatabricksSyncRouter
|
[SecretSync.Databricks]: registerDatabricksSyncRouter,
|
||||||
|
[SecretSync.Humanitec]: registerHumanitecSyncRouter
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import { AzureKeyVaultSyncListItemSchema, AzureKeyVaultSyncSchema } from "@app/s
|
|||||||
import { DatabricksSyncListItemSchema, DatabricksSyncSchema } from "@app/services/secret-sync/databricks";
|
import { DatabricksSyncListItemSchema, DatabricksSyncSchema } from "@app/services/secret-sync/databricks";
|
||||||
import { GcpSyncListItemSchema, GcpSyncSchema } from "@app/services/secret-sync/gcp";
|
import { GcpSyncListItemSchema, GcpSyncSchema } from "@app/services/secret-sync/gcp";
|
||||||
import { GitHubSyncListItemSchema, GitHubSyncSchema } from "@app/services/secret-sync/github";
|
import { GitHubSyncListItemSchema, GitHubSyncSchema } from "@app/services/secret-sync/github";
|
||||||
|
import { HumanitecSyncListItemSchema, HumanitecSyncSchema } from "@app/services/secret-sync/humanitec";
|
||||||
|
|
||||||
const SecretSyncSchema = z.discriminatedUnion("destination", [
|
const SecretSyncSchema = z.discriminatedUnion("destination", [
|
||||||
AwsParameterStoreSyncSchema,
|
AwsParameterStoreSyncSchema,
|
||||||
@@ -29,7 +30,8 @@ const SecretSyncSchema = z.discriminatedUnion("destination", [
|
|||||||
GcpSyncSchema,
|
GcpSyncSchema,
|
||||||
AzureKeyVaultSyncSchema,
|
AzureKeyVaultSyncSchema,
|
||||||
AzureAppConfigurationSyncSchema,
|
AzureAppConfigurationSyncSchema,
|
||||||
DatabricksSyncSchema
|
DatabricksSyncSchema,
|
||||||
|
HumanitecSyncSchema
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const SecretSyncOptionsSchema = z.discriminatedUnion("destination", [
|
const SecretSyncOptionsSchema = z.discriminatedUnion("destination", [
|
||||||
@@ -39,7 +41,8 @@ const SecretSyncOptionsSchema = z.discriminatedUnion("destination", [
|
|||||||
GcpSyncListItemSchema,
|
GcpSyncListItemSchema,
|
||||||
AzureKeyVaultSyncListItemSchema,
|
AzureKeyVaultSyncListItemSchema,
|
||||||
AzureAppConfigurationSyncListItemSchema,
|
AzureAppConfigurationSyncListItemSchema,
|
||||||
DatabricksSyncListItemSchema
|
DatabricksSyncListItemSchema,
|
||||||
|
HumanitecSyncListItemSchema
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const registerSecretSyncRouter = async (server: FastifyZodProvider) => {
|
export const registerSecretSyncRouter = async (server: FastifyZodProvider) => {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import ms from "ms";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -8,6 +7,7 @@ import {
|
|||||||
ProjectUserMembershipRolesSchema
|
ProjectUserMembershipRolesSchema
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
import { PROJECTS } from "@app/lib/api-docs";
|
import { PROJECTS } from "@app/lib/api-docs";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import ms from "ms";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -9,6 +8,7 @@ import {
|
|||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
import { ORGANIZATIONS, PROJECT_IDENTITIES } from "@app/lib/api-docs";
|
import { ORGANIZATIONS, PROJECT_IDENTITIES } from "@app/lib/api-docs";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { OrderByDirection } from "@app/lib/types";
|
import { OrderByDirection } from "@app/lib/types";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ export enum AppConnection {
|
|||||||
Databricks = "databricks",
|
Databricks = "databricks",
|
||||||
GCP = "gcp",
|
GCP = "gcp",
|
||||||
AzureKeyVault = "azure-key-vault",
|
AzureKeyVault = "azure-key-vault",
|
||||||
AzureAppConfiguration = "azure-app-configuration"
|
AzureAppConfiguration = "azure-app-configuration",
|
||||||
|
Humanitec = "humanitec"
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum AWSRegion {
|
export enum AWSRegion {
|
||||||
|
|||||||
@@ -35,6 +35,11 @@ import {
|
|||||||
getAzureKeyVaultConnectionListItem,
|
getAzureKeyVaultConnectionListItem,
|
||||||
validateAzureKeyVaultConnectionCredentials
|
validateAzureKeyVaultConnectionCredentials
|
||||||
} from "./azure-key-vault";
|
} from "./azure-key-vault";
|
||||||
|
import {
|
||||||
|
getHumanitecConnectionListItem,
|
||||||
|
HumanitecConnectionMethod,
|
||||||
|
validateHumanitecConnectionCredentials
|
||||||
|
} from "./humanitec";
|
||||||
|
|
||||||
export const listAppConnectionOptions = () => {
|
export const listAppConnectionOptions = () => {
|
||||||
return [
|
return [
|
||||||
@@ -43,7 +48,8 @@ export const listAppConnectionOptions = () => {
|
|||||||
getGcpConnectionListItem(),
|
getGcpConnectionListItem(),
|
||||||
getAzureKeyVaultConnectionListItem(),
|
getAzureKeyVaultConnectionListItem(),
|
||||||
getAzureAppConfigurationConnectionListItem(),
|
getAzureAppConfigurationConnectionListItem(),
|
||||||
getDatabricksConnectionListItem()
|
getDatabricksConnectionListItem(),
|
||||||
|
getHumanitecConnectionListItem()
|
||||||
].sort((a, b) => a.name.localeCompare(b.name));
|
].sort((a, b) => a.name.localeCompare(b.name));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -106,6 +112,8 @@ export const validateAppConnectionCredentials = async (
|
|||||||
return validateAzureKeyVaultConnectionCredentials(appConnection);
|
return validateAzureKeyVaultConnectionCredentials(appConnection);
|
||||||
case AppConnection.AzureAppConfiguration:
|
case AppConnection.AzureAppConfiguration:
|
||||||
return validateAzureAppConfigurationConnectionCredentials(appConnection);
|
return validateAzureAppConfigurationConnectionCredentials(appConnection);
|
||||||
|
case AppConnection.Humanitec:
|
||||||
|
return validateHumanitecConnectionCredentials(appConnection);
|
||||||
default:
|
default:
|
||||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||||
throw new Error(`Unhandled App Connection ${app}`);
|
throw new Error(`Unhandled App Connection ${app}`);
|
||||||
@@ -128,6 +136,8 @@ export const getAppConnectionMethodName = (method: TAppConnection["method"]) =>
|
|||||||
return "Service Account Impersonation";
|
return "Service Account Impersonation";
|
||||||
case DatabricksConnectionMethod.ServicePrincipal:
|
case DatabricksConnectionMethod.ServicePrincipal:
|
||||||
return "Service Principal";
|
return "Service Principal";
|
||||||
|
case HumanitecConnectionMethod.API_TOKEN:
|
||||||
|
return "API Token";
|
||||||
default:
|
default:
|
||||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||||
throw new Error(`Unhandled App Connection Method: ${method}`);
|
throw new Error(`Unhandled App Connection Method: ${method}`);
|
||||||
|
|||||||
@@ -6,5 +6,6 @@ export const APP_CONNECTION_NAME_MAP: Record<AppConnection, string> = {
|
|||||||
[AppConnection.GCP]: "GCP",
|
[AppConnection.GCP]: "GCP",
|
||||||
[AppConnection.AzureKeyVault]: "Azure Key Vault",
|
[AppConnection.AzureKeyVault]: "Azure Key Vault",
|
||||||
[AppConnection.AzureAppConfiguration]: "Azure App Configuration",
|
[AppConnection.AzureAppConfiguration]: "Azure App Configuration",
|
||||||
[AppConnection.Databricks]: "Databricks"
|
[AppConnection.Databricks]: "Databricks",
|
||||||
|
[AppConnection.Humanitec]: "Humanitec"
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ import { ValidateGcpConnectionCredentialsSchema } from "./gcp";
|
|||||||
import { gcpConnectionService } from "./gcp/gcp-connection-service";
|
import { gcpConnectionService } from "./gcp/gcp-connection-service";
|
||||||
import { ValidateGitHubConnectionCredentialsSchema } from "./github";
|
import { ValidateGitHubConnectionCredentialsSchema } from "./github";
|
||||||
import { githubConnectionService } from "./github/github-connection-service";
|
import { githubConnectionService } from "./github/github-connection-service";
|
||||||
|
import { ValidateHumanitecConnectionCredentialsSchema } from "./humanitec";
|
||||||
|
import { humanitecConnectionService } from "./humanitec/humanitec-connection-service";
|
||||||
|
|
||||||
export type TAppConnectionServiceFactoryDep = {
|
export type TAppConnectionServiceFactoryDep = {
|
||||||
appConnectionDAL: TAppConnectionDALFactory;
|
appConnectionDAL: TAppConnectionDALFactory;
|
||||||
@@ -50,7 +52,8 @@ const VALIDATE_APP_CONNECTION_CREDENTIALS_MAP: Record<AppConnection, TValidateAp
|
|||||||
[AppConnection.GCP]: ValidateGcpConnectionCredentialsSchema,
|
[AppConnection.GCP]: ValidateGcpConnectionCredentialsSchema,
|
||||||
[AppConnection.AzureKeyVault]: ValidateAzureKeyVaultConnectionCredentialsSchema,
|
[AppConnection.AzureKeyVault]: ValidateAzureKeyVaultConnectionCredentialsSchema,
|
||||||
[AppConnection.AzureAppConfiguration]: ValidateAzureAppConfigurationConnectionCredentialsSchema,
|
[AppConnection.AzureAppConfiguration]: ValidateAzureAppConfigurationConnectionCredentialsSchema,
|
||||||
[AppConnection.Databricks]: ValidateDatabricksConnectionCredentialsSchema
|
[AppConnection.Databricks]: ValidateDatabricksConnectionCredentialsSchema,
|
||||||
|
[AppConnection.Humanitec]: ValidateHumanitecConnectionCredentialsSchema
|
||||||
};
|
};
|
||||||
|
|
||||||
export const appConnectionServiceFactory = ({
|
export const appConnectionServiceFactory = ({
|
||||||
@@ -371,6 +374,7 @@ export const appConnectionServiceFactory = ({
|
|||||||
github: githubConnectionService(connectAppConnectionById),
|
github: githubConnectionService(connectAppConnectionById),
|
||||||
gcp: gcpConnectionService(connectAppConnectionById),
|
gcp: gcpConnectionService(connectAppConnectionById),
|
||||||
databricks: databricksConnectionService(connectAppConnectionById, appConnectionDAL, kmsService),
|
databricks: databricksConnectionService(connectAppConnectionById, appConnectionDAL, kmsService),
|
||||||
aws: awsConnectionService(connectAppConnectionById)
|
aws: awsConnectionService(connectAppConnectionById),
|
||||||
|
humanitec: humanitecConnectionService(connectAppConnectionById)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -32,6 +32,12 @@ import {
|
|||||||
TValidateAzureKeyVaultConnectionCredentials
|
TValidateAzureKeyVaultConnectionCredentials
|
||||||
} from "./azure-key-vault";
|
} from "./azure-key-vault";
|
||||||
import { TGcpConnection, TGcpConnectionConfig, TGcpConnectionInput, TValidateGcpConnectionCredentials } from "./gcp";
|
import { TGcpConnection, TGcpConnectionConfig, TGcpConnectionInput, TValidateGcpConnectionCredentials } from "./gcp";
|
||||||
|
import {
|
||||||
|
THumanitecConnection,
|
||||||
|
THumanitecConnectionConfig,
|
||||||
|
THumanitecConnectionInput,
|
||||||
|
TValidateHumanitecConnectionCredentials
|
||||||
|
} from "./humanitec";
|
||||||
|
|
||||||
export type TAppConnection = { id: string } & (
|
export type TAppConnection = { id: string } & (
|
||||||
| TAwsConnection
|
| TAwsConnection
|
||||||
@@ -40,6 +46,7 @@ export type TAppConnection = { id: string } & (
|
|||||||
| TAzureKeyVaultConnection
|
| TAzureKeyVaultConnection
|
||||||
| TAzureAppConfigurationConnection
|
| TAzureAppConfigurationConnection
|
||||||
| TDatabricksConnection
|
| TDatabricksConnection
|
||||||
|
| THumanitecConnection
|
||||||
);
|
);
|
||||||
|
|
||||||
export type TAppConnectionInput = { id: string } & (
|
export type TAppConnectionInput = { id: string } & (
|
||||||
@@ -49,6 +56,7 @@ export type TAppConnectionInput = { id: string } & (
|
|||||||
| TAzureKeyVaultConnectionInput
|
| TAzureKeyVaultConnectionInput
|
||||||
| TAzureAppConfigurationConnectionInput
|
| TAzureAppConfigurationConnectionInput
|
||||||
| TDatabricksConnectionInput
|
| TDatabricksConnectionInput
|
||||||
|
| THumanitecConnectionInput
|
||||||
);
|
);
|
||||||
|
|
||||||
export type TCreateAppConnectionDTO = Pick<
|
export type TCreateAppConnectionDTO = Pick<
|
||||||
@@ -66,7 +74,8 @@ export type TAppConnectionConfig =
|
|||||||
| TGcpConnectionConfig
|
| TGcpConnectionConfig
|
||||||
| TAzureKeyVaultConnectionConfig
|
| TAzureKeyVaultConnectionConfig
|
||||||
| TAzureAppConfigurationConnectionConfig
|
| TAzureAppConfigurationConnectionConfig
|
||||||
| TDatabricksConnectionConfig;
|
| TDatabricksConnectionConfig
|
||||||
|
| THumanitecConnectionConfig;
|
||||||
|
|
||||||
export type TValidateAppConnectionCredentials =
|
export type TValidateAppConnectionCredentials =
|
||||||
| TValidateAwsConnectionCredentials
|
| TValidateAwsConnectionCredentials
|
||||||
@@ -74,7 +83,8 @@ export type TValidateAppConnectionCredentials =
|
|||||||
| TValidateGcpConnectionCredentials
|
| TValidateGcpConnectionCredentials
|
||||||
| TValidateAzureKeyVaultConnectionCredentials
|
| TValidateAzureKeyVaultConnectionCredentials
|
||||||
| TValidateAzureAppConfigurationConnectionCredentials
|
| TValidateAzureAppConfigurationConnectionCredentials
|
||||||
| TValidateDatabricksConnectionCredentials;
|
| TValidateDatabricksConnectionCredentials
|
||||||
|
| TValidateHumanitecConnectionCredentials;
|
||||||
|
|
||||||
export type TListAwsConnectionKmsKeys = {
|
export type TListAwsConnectionKmsKeys = {
|
||||||
connectionId: string;
|
connectionId: string;
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export enum HumanitecConnectionMethod {
|
||||||
|
API_TOKEN = "api-token"
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
import { AxiosError, AxiosResponse } from "axios";
|
||||||
|
|
||||||
|
import { request } from "@app/lib/config/request";
|
||||||
|
import { BadRequestError, InternalServerError } from "@app/lib/errors";
|
||||||
|
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||||
|
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||||
|
|
||||||
|
import { HumanitecConnectionMethod } from "./humanitec-connection-enums";
|
||||||
|
import {
|
||||||
|
HumanitecApp,
|
||||||
|
HumanitecOrg,
|
||||||
|
HumanitecOrgWithApps,
|
||||||
|
THumanitecConnection,
|
||||||
|
THumanitecConnectionConfig
|
||||||
|
} from "./humanitec-connection-types";
|
||||||
|
|
||||||
|
export const getHumanitecConnectionListItem = () => {
|
||||||
|
return {
|
||||||
|
name: "Humanitec" as const,
|
||||||
|
app: AppConnection.Humanitec as const,
|
||||||
|
methods: Object.values(HumanitecConnectionMethod) as [HumanitecConnectionMethod.API_TOKEN]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const validateHumanitecConnectionCredentials = async (config: THumanitecConnectionConfig) => {
|
||||||
|
const { credentials: inputCredentials } = config;
|
||||||
|
|
||||||
|
let response: AxiosResponse<HumanitecOrg[]> | null = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
response = await request.get<HumanitecOrg[]>(`${IntegrationUrls.HUMANITEC_API_URL}/orgs`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${inputCredentials.apiToken}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error: unknown) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `Failed to validate credentials: ${error.message || "Unknown error"}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Unable to validate connection - verify credentials"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response?.data) {
|
||||||
|
throw new InternalServerError({
|
||||||
|
message: "Failed to get organizations: Response was empty"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return inputCredentials;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const listOrganizations = async (appConnection: THumanitecConnection): Promise<HumanitecOrgWithApps[]> => {
|
||||||
|
const {
|
||||||
|
credentials: { apiToken }
|
||||||
|
} = appConnection;
|
||||||
|
const response = await request.get<HumanitecOrg[]>(`${IntegrationUrls.HUMANITEC_API_URL}/orgs`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${apiToken}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.data) {
|
||||||
|
throw new InternalServerError({
|
||||||
|
message: "Failed to get organizations: Response was empty"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const orgs = response.data;
|
||||||
|
const orgsWithApps: HumanitecOrgWithApps[] = [];
|
||||||
|
|
||||||
|
for (const org of orgs) {
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
const appsResponse = await request.get<HumanitecApp[]>(`${IntegrationUrls.HUMANITEC_API_URL}/orgs/${org.id}/apps`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${apiToken}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (appsResponse.data) {
|
||||||
|
const apps = appsResponse.data;
|
||||||
|
orgsWithApps.push({
|
||||||
|
...org,
|
||||||
|
apps: apps.map((app) => ({
|
||||||
|
name: app.name,
|
||||||
|
id: app.id,
|
||||||
|
envs: app.envs
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return orgsWithApps;
|
||||||
|
};
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import z from "zod";
|
||||||
|
|
||||||
|
import { AppConnections } from "@app/lib/api-docs";
|
||||||
|
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||||
|
import {
|
||||||
|
BaseAppConnectionSchema,
|
||||||
|
GenericCreateAppConnectionFieldsSchema,
|
||||||
|
GenericUpdateAppConnectionFieldsSchema
|
||||||
|
} from "@app/services/app-connection/app-connection-schemas";
|
||||||
|
|
||||||
|
import { HumanitecConnectionMethod } from "./humanitec-connection-enums";
|
||||||
|
|
||||||
|
export const HumanitecConnectionAccessTokenCredentialsSchema = z.object({
|
||||||
|
apiToken: z.string().trim().min(1, "API Token required")
|
||||||
|
});
|
||||||
|
|
||||||
|
const BaseHumanitecConnectionSchema = BaseAppConnectionSchema.extend({ app: z.literal(AppConnection.Humanitec) });
|
||||||
|
|
||||||
|
export const HumanitecConnectionSchema = BaseHumanitecConnectionSchema.extend({
|
||||||
|
method: z.literal(HumanitecConnectionMethod.API_TOKEN),
|
||||||
|
credentials: HumanitecConnectionAccessTokenCredentialsSchema
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SanitizedHumanitecConnectionSchema = z.discriminatedUnion("method", [
|
||||||
|
BaseHumanitecConnectionSchema.extend({
|
||||||
|
method: z.literal(HumanitecConnectionMethod.API_TOKEN),
|
||||||
|
credentials: HumanitecConnectionAccessTokenCredentialsSchema.pick({})
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const ValidateHumanitecConnectionCredentialsSchema = z.discriminatedUnion("method", [
|
||||||
|
z.object({
|
||||||
|
method: z
|
||||||
|
.literal(HumanitecConnectionMethod.API_TOKEN)
|
||||||
|
.describe(AppConnections?.CREATE(AppConnection.Humanitec).method),
|
||||||
|
credentials: HumanitecConnectionAccessTokenCredentialsSchema.describe(
|
||||||
|
AppConnections.CREATE(AppConnection.Humanitec).credentials
|
||||||
|
)
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const CreateHumanitecConnectionSchema = ValidateHumanitecConnectionCredentialsSchema.and(
|
||||||
|
GenericCreateAppConnectionFieldsSchema(AppConnection.Humanitec)
|
||||||
|
);
|
||||||
|
|
||||||
|
export const UpdateHumanitecConnectionSchema = z
|
||||||
|
.object({
|
||||||
|
credentials: HumanitecConnectionAccessTokenCredentialsSchema.optional().describe(
|
||||||
|
AppConnections.UPDATE(AppConnection.Humanitec).credentials
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.and(GenericUpdateAppConnectionFieldsSchema(AppConnection.Humanitec));
|
||||||
|
|
||||||
|
export const HumanitecConnectionListItemSchema = z.object({
|
||||||
|
name: z.literal("Humanitec"),
|
||||||
|
app: z.literal(AppConnection.Humanitec),
|
||||||
|
methods: z.nativeEnum(HumanitecConnectionMethod).array()
|
||||||
|
});
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { logger } from "@app/lib/logger";
|
||||||
|
import { OrgServiceActor } from "@app/lib/types";
|
||||||
|
|
||||||
|
import { AppConnection } from "../app-connection-enums";
|
||||||
|
import { listOrganizations as getHumanitecOrganizations } from "./humanitec-connection-fns";
|
||||||
|
import { THumanitecConnection } from "./humanitec-connection-types";
|
||||||
|
|
||||||
|
type TGetAppConnectionFunc = (
|
||||||
|
app: AppConnection,
|
||||||
|
connectionId: string,
|
||||||
|
actor: OrgServiceActor
|
||||||
|
) => Promise<THumanitecConnection>;
|
||||||
|
|
||||||
|
export const humanitecConnectionService = (getAppConnection: TGetAppConnectionFunc) => {
|
||||||
|
const listOrganizations = async (connectionId: string, actor: OrgServiceActor) => {
|
||||||
|
const appConnection = await getAppConnection(AppConnection.Humanitec, connectionId, actor);
|
||||||
|
try {
|
||||||
|
const organizations = await getHumanitecOrganizations(appConnection);
|
||||||
|
return organizations;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(error, "Failed to establish connection with Humanitec");
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
listOrganizations
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import z from "zod";
|
||||||
|
|
||||||
|
import { DiscriminativePick } from "@app/lib/types";
|
||||||
|
|
||||||
|
import { AppConnection } from "../app-connection-enums";
|
||||||
|
import {
|
||||||
|
CreateHumanitecConnectionSchema,
|
||||||
|
HumanitecConnectionSchema,
|
||||||
|
ValidateHumanitecConnectionCredentialsSchema
|
||||||
|
} from "./humanitec-connection-schemas";
|
||||||
|
|
||||||
|
export type THumanitecConnection = z.infer<typeof HumanitecConnectionSchema>;
|
||||||
|
|
||||||
|
export type THumanitecConnectionInput = z.infer<typeof CreateHumanitecConnectionSchema> & {
|
||||||
|
app: AppConnection.Humanitec;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TValidateHumanitecConnectionCredentials = typeof ValidateHumanitecConnectionCredentialsSchema;
|
||||||
|
|
||||||
|
export type THumanitecConnectionConfig = DiscriminativePick<
|
||||||
|
THumanitecConnectionInput,
|
||||||
|
"method" | "app" | "credentials"
|
||||||
|
> & {
|
||||||
|
orgId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type HumanitecOrg = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type HumanitecApp = {
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
envs: { name: string; id: string }[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type HumanitecOrgWithApps = HumanitecOrg & {
|
||||||
|
apps: HumanitecApp[];
|
||||||
|
};
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export * from "./humanitec-connection-enums";
|
||||||
|
export * from "./humanitec-connection-fns";
|
||||||
|
export * from "./humanitec-connection-schemas";
|
||||||
|
export * from "./humanitec-connection-types";
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import * as x509 from "@peculiar/x509";
|
import * as x509 from "@peculiar/x509";
|
||||||
import crypto, { KeyObject } from "crypto";
|
import crypto, { KeyObject } from "crypto";
|
||||||
import ms from "ms";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { ActionProjectType, ProjectType, TCertificateAuthorities, TCertificateTemplates } from "@app/db/schemas";
|
import { ActionProjectType, ProjectType, TCertificateAuthorities, TCertificateTemplates } from "@app/db/schemas";
|
||||||
@@ -10,6 +9,7 @@ import { TPermissionServiceFactory } from "@app/ee/services/permission/permissio
|
|||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { TCertificateBodyDALFactory } from "@app/services/certificate/certificate-body-dal";
|
import { TCertificateBodyDALFactory } from "@app/services/certificate/certificate-body-dal";
|
||||||
import { TCertificateDALFactory } from "@app/services/certificate/certificate-dal";
|
import { TCertificateDALFactory } from "@app/services/certificate/certificate-dal";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import ms from "ms";
|
|
||||||
|
|
||||||
import { TCertificateTemplates } from "@app/db/schemas";
|
import { TCertificateTemplates } from "@app/db/schemas";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
|
|
||||||
export const validateCertificateDetailsAgainstTemplate = (
|
export const validateCertificateDetailsAgainstTemplate = (
|
||||||
cert: {
|
cert: {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import ms from "ms";
|
|
||||||
|
|
||||||
import { ActionProjectType, ProjectMembershipRole, SecretKeyEncoding, TGroups } from "@app/db/schemas";
|
import { ActionProjectType, ProjectMembershipRole, SecretKeyEncoding, TGroups } from "@app/db/schemas";
|
||||||
import {
|
import {
|
||||||
@@ -12,6 +11,7 @@ import { decryptAsymmetric, encryptAsymmetric } from "@app/lib/crypto";
|
|||||||
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
|
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { groupBy } from "@app/lib/fn";
|
import { groupBy } from "@app/lib/fn";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { isUuidV4 } from "@app/lib/validator";
|
import { isUuidV4 } from "@app/lib/validator";
|
||||||
|
|
||||||
import { TGroupDALFactory } from "../../ee/services/group/group-dal";
|
import { TGroupDALFactory } from "../../ee/services/group/group-dal";
|
||||||
|
|||||||
@@ -7,4 +7,9 @@ export type TIdentityAccessTokenJwtPayload = {
|
|||||||
clientSecretId: string;
|
clientSecretId: string;
|
||||||
identityAccessTokenId: string;
|
identityAccessTokenId: string;
|
||||||
authTokenType: string;
|
authTokenType: string;
|
||||||
|
identityAuth: {
|
||||||
|
oidc?: {
|
||||||
|
claims: Record<string, string>;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { TPermissionServiceFactory } from "@app/ee/services/permission/permissio
|
|||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors";
|
||||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||||
|
import { getStringValueByDot } from "@app/lib/template/dot-access";
|
||||||
|
|
||||||
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||||
@@ -180,8 +181,9 @@ export const identityJwtAuthServiceFactory = ({
|
|||||||
if (identityJwtAuth.boundClaims) {
|
if (identityJwtAuth.boundClaims) {
|
||||||
Object.keys(identityJwtAuth.boundClaims).forEach((claimKey) => {
|
Object.keys(identityJwtAuth.boundClaims).forEach((claimKey) => {
|
||||||
const claimValue = (identityJwtAuth.boundClaims as Record<string, string>)[claimKey];
|
const claimValue = (identityJwtAuth.boundClaims as Record<string, string>)[claimKey];
|
||||||
|
const value = getStringValueByDot(tokenData, claimKey) || "";
|
||||||
|
|
||||||
if (!tokenData[claimKey]) {
|
if (!value) {
|
||||||
throw new UnauthorizedError({
|
throw new UnauthorizedError({
|
||||||
message: `Access denied: token has no ${claimKey} field`
|
message: `Access denied: token has no ${claimKey} field`
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { TPermissionServiceFactory } from "@app/ee/services/permission/permissio
|
|||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors";
|
||||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||||
|
import { getStringValueByDot } from "@app/lib/template/dot-access";
|
||||||
|
|
||||||
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||||
@@ -80,7 +81,7 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
const { data: discoveryDoc } = await axios.get<{ jwks_uri: string }>(
|
const { data: discoveryDoc } = await axios.get<{ jwks_uri: string }>(
|
||||||
`${identityOidcAuth.oidcDiscoveryUrl}/.well-known/openid-configuration`,
|
`${identityOidcAuth.oidcDiscoveryUrl}/.well-known/openid-configuration`,
|
||||||
{
|
{
|
||||||
httpsAgent: requestAgent
|
httpsAgent: identityOidcAuth.oidcDiscoveryUrl.includes("https") ? requestAgent : undefined
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const jwksUri = discoveryDoc.jwks_uri;
|
const jwksUri = discoveryDoc.jwks_uri;
|
||||||
@@ -94,7 +95,7 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
|
|
||||||
const client = new JwksClient({
|
const client = new JwksClient({
|
||||||
jwksUri,
|
jwksUri,
|
||||||
requestAgent
|
requestAgent: identityOidcAuth.oidcDiscoveryUrl.includes("https") ? requestAgent : undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
const { kid } = decodedToken.header;
|
const { kid } = decodedToken.header;
|
||||||
@@ -111,7 +112,6 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
message: `Access denied: ${error.message}`
|
message: `Access denied: ${error.message}`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,10 +138,16 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
if (identityOidcAuth.boundClaims) {
|
if (identityOidcAuth.boundClaims) {
|
||||||
Object.keys(identityOidcAuth.boundClaims).forEach((claimKey) => {
|
Object.keys(identityOidcAuth.boundClaims).forEach((claimKey) => {
|
||||||
const claimValue = (identityOidcAuth.boundClaims as Record<string, string>)[claimKey];
|
const claimValue = (identityOidcAuth.boundClaims as Record<string, string>)[claimKey];
|
||||||
|
const value = getStringValueByDot(tokenData, claimKey) || "";
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
throw new UnauthorizedError({
|
||||||
|
message: `Access denied: token has no ${claimKey} field`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// handle both single and multi-valued claims
|
// handle both single and multi-valued claims
|
||||||
if (
|
if (!claimValue.split(", ").some((claimEntry) => doesFieldValueMatchOidcPolicy(value, claimEntry))) {
|
||||||
!claimValue.split(", ").some((claimEntry) => doesFieldValueMatchOidcPolicy(tokenData[claimKey], claimEntry))
|
|
||||||
) {
|
|
||||||
throw new UnauthorizedError({
|
throw new UnauthorizedError({
|
||||||
message: "Access denied: OIDC claim not allowed."
|
message: "Access denied: OIDC claim not allowed."
|
||||||
});
|
});
|
||||||
@@ -149,6 +155,20 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const filteredClaims: Record<string, string> = {};
|
||||||
|
if (identityOidcAuth.claimMetadataMapping) {
|
||||||
|
Object.keys(identityOidcAuth.claimMetadataMapping).forEach((permissionKey) => {
|
||||||
|
const claimKey = (identityOidcAuth.claimMetadataMapping as Record<string, string>)[permissionKey];
|
||||||
|
const value = getStringValueByDot(tokenData, claimKey) || "";
|
||||||
|
if (!value) {
|
||||||
|
throw new UnauthorizedError({
|
||||||
|
message: `Access denied: token has no ${claimKey} field`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
filteredClaims[permissionKey] = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const identityAccessToken = await identityOidcAuthDAL.transaction(async (tx) => {
|
const identityAccessToken = await identityOidcAuthDAL.transaction(async (tx) => {
|
||||||
const newToken = await identityAccessTokenDAL.create(
|
const newToken = await identityAccessTokenDAL.create(
|
||||||
{
|
{
|
||||||
@@ -170,7 +190,12 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
{
|
{
|
||||||
identityId: identityOidcAuth.identityId,
|
identityId: identityOidcAuth.identityId,
|
||||||
identityAccessTokenId: identityAccessToken.id,
|
identityAccessTokenId: identityAccessToken.id,
|
||||||
authTokenType: AuthTokenType.IDENTITY_ACCESS_TOKEN
|
authTokenType: AuthTokenType.IDENTITY_ACCESS_TOKEN,
|
||||||
|
identityAuth: {
|
||||||
|
oidc: {
|
||||||
|
claims: filteredClaims
|
||||||
|
}
|
||||||
|
}
|
||||||
} as TIdentityAccessTokenJwtPayload,
|
} as TIdentityAccessTokenJwtPayload,
|
||||||
appCfg.AUTH_SECRET,
|
appCfg.AUTH_SECRET,
|
||||||
// akhilmhdh: for non-expiry tokens you should not even set the value, including undefined. Even for undefined jsonwebtoken throws error
|
// akhilmhdh: for non-expiry tokens you should not even set the value, including undefined. Even for undefined jsonwebtoken throws error
|
||||||
@@ -191,6 +216,7 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
boundIssuer,
|
boundIssuer,
|
||||||
boundAudiences,
|
boundAudiences,
|
||||||
boundClaims,
|
boundClaims,
|
||||||
|
claimMetadataMapping,
|
||||||
boundSubject,
|
boundSubject,
|
||||||
accessTokenTTL,
|
accessTokenTTL,
|
||||||
accessTokenMaxTTL,
|
accessTokenMaxTTL,
|
||||||
@@ -257,6 +283,7 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
boundIssuer,
|
boundIssuer,
|
||||||
boundAudiences,
|
boundAudiences,
|
||||||
boundClaims,
|
boundClaims,
|
||||||
|
claimMetadataMapping,
|
||||||
boundSubject,
|
boundSubject,
|
||||||
accessTokenMaxTTL,
|
accessTokenMaxTTL,
|
||||||
accessTokenTTL,
|
accessTokenTTL,
|
||||||
@@ -277,6 +304,7 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
boundIssuer,
|
boundIssuer,
|
||||||
boundAudiences,
|
boundAudiences,
|
||||||
boundClaims,
|
boundClaims,
|
||||||
|
claimMetadataMapping,
|
||||||
boundSubject,
|
boundSubject,
|
||||||
accessTokenTTL,
|
accessTokenTTL,
|
||||||
accessTokenMaxTTL,
|
accessTokenMaxTTL,
|
||||||
@@ -338,6 +366,7 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
boundIssuer,
|
boundIssuer,
|
||||||
boundAudiences,
|
boundAudiences,
|
||||||
boundClaims,
|
boundClaims,
|
||||||
|
claimMetadataMapping,
|
||||||
boundSubject,
|
boundSubject,
|
||||||
accessTokenMaxTTL,
|
accessTokenMaxTTL,
|
||||||
accessTokenTTL,
|
accessTokenTTL,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export type TAttachOidcAuthDTO = {
|
|||||||
boundIssuer: string;
|
boundIssuer: string;
|
||||||
boundAudiences: string;
|
boundAudiences: string;
|
||||||
boundClaims: Record<string, string>;
|
boundClaims: Record<string, string>;
|
||||||
|
claimMetadataMapping?: Record<string, string>;
|
||||||
boundSubject: string;
|
boundSubject: string;
|
||||||
accessTokenTTL: number;
|
accessTokenTTL: number;
|
||||||
accessTokenMaxTTL: number;
|
accessTokenMaxTTL: number;
|
||||||
@@ -21,6 +22,7 @@ export type TUpdateOidcAuthDTO = {
|
|||||||
boundIssuer?: string;
|
boundIssuer?: string;
|
||||||
boundAudiences?: string;
|
boundAudiences?: string;
|
||||||
boundClaims?: Record<string, string>;
|
boundClaims?: Record<string, string>;
|
||||||
|
claimMetadataMapping?: Record<string, string>;
|
||||||
boundSubject?: string;
|
boundSubject?: string;
|
||||||
accessTokenTTL?: number;
|
accessTokenTTL?: number;
|
||||||
accessTokenMaxTTL?: number;
|
accessTokenMaxTTL?: number;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { ForbiddenError, subject } from "@casl/ability";
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
import ms from "ms";
|
|
||||||
|
|
||||||
import { ActionProjectType, ProjectMembershipRole } from "@app/db/schemas";
|
import { ActionProjectType, ProjectMembershipRole } from "@app/db/schemas";
|
||||||
import {
|
import {
|
||||||
@@ -10,6 +9,7 @@ import { TPermissionServiceFactory } from "@app/ee/services/permission/permissio
|
|||||||
import { ProjectPermissionIdentityActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionIdentityActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { groupBy } from "@app/lib/fn";
|
import { groupBy } from "@app/lib/fn";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
|
|
||||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||||
import { TProjectDALFactory } from "../project/project-dal";
|
import { TProjectDALFactory } from "../project/project-dal";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import { TableName, TIdentities } from "@app/db/schemas";
|
import { TableName, TIdentities } from "@app/db/schemas";
|
||||||
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
|
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
||||||
|
|
||||||
export type TIdentityDALFactory = ReturnType<typeof identityDALFactory>;
|
export type TIdentityDALFactory = ReturnType<typeof identityDALFactory>;
|
||||||
|
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ export enum IntegrationUrls {
|
|||||||
NORTHFLANK_API_URL = "https://api.northflank.com",
|
NORTHFLANK_API_URL = "https://api.northflank.com",
|
||||||
HASURA_CLOUD_API_URL = "https://data.pro.hasura.io/v1/graphql",
|
HASURA_CLOUD_API_URL = "https://data.pro.hasura.io/v1/graphql",
|
||||||
AZURE_DEVOPS_API_URL = "https://dev.azure.com",
|
AZURE_DEVOPS_API_URL = "https://dev.azure.com",
|
||||||
|
HUMANITEC_API_URL = "https://api.humanitec.io",
|
||||||
|
|
||||||
GCP_SECRET_MANAGER_SERVICE_NAME = "secretmanager.googleapis.com",
|
GCP_SECRET_MANAGER_SERVICE_NAME = "secretmanager.googleapis.com",
|
||||||
GCP_SECRET_MANAGER_URL = `https://${GCP_SECRET_MANAGER_SERVICE_NAME}`,
|
GCP_SECRET_MANAGER_URL = `https://${GCP_SECRET_MANAGER_SERVICE_NAME}`,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { TOidcConfigDALFactory } from "@app/ee/services/oidc/oidc-config-dal";
|
|||||||
import {
|
import {
|
||||||
OrgPermissionActions,
|
OrgPermissionActions,
|
||||||
OrgPermissionGroupActions,
|
OrgPermissionGroupActions,
|
||||||
|
OrgPermissionSecretShareAction,
|
||||||
OrgPermissionSubjects
|
OrgPermissionSubjects
|
||||||
} from "@app/ee/services/permission/org-permission";
|
} from "@app/ee/services/permission/org-permission";
|
||||||
import {
|
import {
|
||||||
@@ -334,12 +335,27 @@ export const orgServiceFactory = ({
|
|||||||
actorOrgId,
|
actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
orgId,
|
orgId,
|
||||||
data: { name, slug, authEnforced, scimEnabled, defaultMembershipRoleSlug, enforceMfa, selectedMfaMethod }
|
data: {
|
||||||
|
name,
|
||||||
|
slug,
|
||||||
|
authEnforced,
|
||||||
|
scimEnabled,
|
||||||
|
defaultMembershipRoleSlug,
|
||||||
|
enforceMfa,
|
||||||
|
selectedMfaMethod,
|
||||||
|
allowSecretSharingOutsideOrganization
|
||||||
|
}
|
||||||
}: TUpdateOrgDTO) => {
|
}: TUpdateOrgDTO) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings);
|
||||||
|
|
||||||
|
if (allowSecretSharingOutsideOrganization !== undefined) {
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
OrgPermissionSecretShareAction.ManageSettings,
|
||||||
|
OrgPermissionSubjects.SecretShare
|
||||||
|
);
|
||||||
|
}
|
||||||
const plan = await licenseService.getPlan(orgId);
|
const plan = await licenseService.getPlan(orgId);
|
||||||
const currentOrg = await orgDAL.findOrgById(actorOrgId);
|
const currentOrg = await orgDAL.findOrgById(actorOrgId);
|
||||||
|
|
||||||
@@ -406,7 +422,8 @@ export const orgServiceFactory = ({
|
|||||||
scimEnabled,
|
scimEnabled,
|
||||||
defaultMembershipRole,
|
defaultMembershipRole,
|
||||||
enforceMfa,
|
enforceMfa,
|
||||||
selectedMfaMethod
|
selectedMfaMethod,
|
||||||
|
allowSecretSharingOutsideOrganization
|
||||||
});
|
});
|
||||||
if (!org) throw new NotFoundError({ message: `Organization with ID '${orgId}' not found` });
|
if (!org) throw new NotFoundError({ message: `Organization with ID '${orgId}' not found` });
|
||||||
return org;
|
return org;
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ export type TUpdateOrgDTO = {
|
|||||||
defaultMembershipRoleSlug: string;
|
defaultMembershipRoleSlug: string;
|
||||||
enforceMfa: boolean;
|
enforceMfa: boolean;
|
||||||
selectedMfaMethod: MfaMethod;
|
selectedMfaMethod: MfaMethod;
|
||||||
|
allowSecretSharingOutsideOrganization: boolean;
|
||||||
}>;
|
}>;
|
||||||
} & TOrgPermission;
|
} & TOrgPermission;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import ms from "ms";
|
|
||||||
|
|
||||||
import { ActionProjectType, ProjectMembershipRole, ProjectVersion, TableName } from "@app/db/schemas";
|
import { ActionProjectType, ProjectMembershipRole, ProjectVersion, TableName } from "@app/db/schemas";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
@@ -14,6 +13,7 @@ import { TProjectUserAdditionalPrivilegeDALFactory } from "@app/ee/services/proj
|
|||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { groupBy } from "@app/lib/fn";
|
import { groupBy } from "@app/lib/fn";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
|
|
||||||
import { TUserGroupMembershipDALFactory } from "../../ee/services/group/user-group-membership-dal";
|
import { TUserGroupMembershipDALFactory } from "../../ee/services/group/user-group-membership-dal";
|
||||||
import { ActorType } from "../auth/auth-type";
|
import { ActorType } from "../auth/auth-type";
|
||||||
|
|||||||
@@ -82,6 +82,13 @@ export const secretSharingServiceFactory = ({
|
|||||||
if (!permission) throw new ForbiddenRequestError({ name: "User is not a part of the specified organization" });
|
if (!permission) throw new ForbiddenRequestError({ name: "User is not a part of the specified organization" });
|
||||||
$validateSharedSecretExpiry(expiresAt);
|
$validateSharedSecretExpiry(expiresAt);
|
||||||
|
|
||||||
|
const org = await orgDAL.findOrgById(orgId);
|
||||||
|
if (!org.allowSecretSharingOutsideOrganization && accessType === SecretSharingAccessType.Anyone) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Organization does not allow sharing secrets to members outside of this organization"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (secretValue.length > 10_000) {
|
if (secretValue.length > 10_000) {
|
||||||
throw new BadRequestError({ message: "Shared secret value too long" });
|
throw new BadRequestError({ message: "Shared secret value too long" });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -382,6 +382,8 @@ export const AwsParameterStoreSyncFns = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (syncOptions.disableSecretDeletion) return;
|
||||||
|
|
||||||
const parametersToDelete: AWS.SSM.Parameter[] = [];
|
const parametersToDelete: AWS.SSM.Parameter[] = [];
|
||||||
|
|
||||||
for (const entry of Object.entries(awsParameterStoreSecretsRecord)) {
|
for (const entry of Object.entries(awsParameterStoreSecretsRecord)) {
|
||||||
|
|||||||
@@ -396,6 +396,8 @@ export const AwsSecretsManagerSyncFns = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (syncOptions.disableSecretDeletion) return;
|
||||||
|
|
||||||
for await (const secretKey of Object.keys(awsSecretsRecord)) {
|
for await (const secretKey of Object.keys(awsSecretsRecord)) {
|
||||||
if (!(secretKey in secretMap) || !secretMap[secretKey].value) {
|
if (!(secretKey in secretMap) || !secretMap[secretKey].value) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
+2
@@ -136,6 +136,8 @@ export const azureAppConfigurationSyncFactory = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||||
|
|
||||||
for await (const key of Object.keys(azureAppConfigSecrets)) {
|
for await (const key of Object.keys(azureAppConfigSecrets)) {
|
||||||
const azureSecret = azureAppConfigSecrets[key];
|
const azureSecret = azureAppConfigSecrets[key];
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -189,6 +189,8 @@ export const azureKeyVaultSyncFactory = ({ kmsService, appConnectionDAL }: TAzur
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||||
|
|
||||||
for await (const deleteSecretKey of deleteSecrets.filter(
|
for await (const deleteSecretKey of deleteSecrets.filter(
|
||||||
(secret) => !setSecrets.find((setSecret) => setSecret.key === secret)
|
(secret) => !setSecrets.find((setSecret) => setSecret.key === secret)
|
||||||
)) {
|
)) {
|
||||||
|
|||||||
@@ -112,6 +112,8 @@ export const databricksSyncFactory = ({ kmsService, appConnectionDAL }: TDatabri
|
|||||||
accessToken
|
accessToken
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||||
|
|
||||||
for await (const secret of databricksSecretKeys) {
|
for await (const secret of databricksSecretKeys) {
|
||||||
if (!(secret.key in secretMap)) {
|
if (!(secret.key in secretMap)) {
|
||||||
await deleteDatabricksSecrets({
|
await deleteDatabricksSecrets({
|
||||||
|
|||||||
@@ -71,8 +71,16 @@ const getGcpSecrets = async (accessToken: string, secretSync: TGcpSyncWithCreden
|
|||||||
|
|
||||||
res[key] = Buffer.from(secretLatest.payload.data, "base64").toString("utf-8");
|
res[key] = Buffer.from(secretLatest.payload.data, "base64").toString("utf-8");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// when a secret in GCP has no versions, we treat it as if it's a blank value
|
// when a secret in GCP has no versions, or is disabled/destroyed, we treat it as if it's a blank value
|
||||||
if (error instanceof AxiosError && error.response?.status === 404) {
|
if (
|
||||||
|
error instanceof AxiosError &&
|
||||||
|
(error.response?.status === 404 ||
|
||||||
|
(error.response?.status === 400 &&
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||||
|
error.response.data.error.status === "FAILED_PRECONDITION" &&
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-call
|
||||||
|
error.response.data.error.message.match(/(?:disabled|destroyed)/i)))
|
||||||
|
) {
|
||||||
res[key] = "";
|
res[key] = "";
|
||||||
} else {
|
} else {
|
||||||
throw new SecretSyncError({
|
throw new SecretSyncError({
|
||||||
@@ -147,6 +155,9 @@ export const GcpSyncFns = {
|
|||||||
for await (const key of Object.keys(gcpSecrets)) {
|
for await (const key of Object.keys(gcpSecrets)) {
|
||||||
try {
|
try {
|
||||||
if (!(key in secretMap) || !secretMap[key].value) {
|
if (!(key in secretMap) || !secretMap[key].value) {
|
||||||
|
// eslint-disable-next-line no-continue
|
||||||
|
if (secretSync.syncOptions.disableSecretDeletion) continue;
|
||||||
|
|
||||||
// case: delete secret
|
// case: delete secret
|
||||||
await request.delete(
|
await request.delete(
|
||||||
`${IntegrationUrls.GCP_SECRET_MANAGER_URL}/v1/projects/${destinationConfig.projectId}/secrets/${key}`,
|
`${IntegrationUrls.GCP_SECRET_MANAGER_URL}/v1/projects/${destinationConfig.projectId}/secrets/${key}`,
|
||||||
|
|||||||
@@ -192,12 +192,6 @@ export const GithubSyncFns = {
|
|||||||
|
|
||||||
const publicKey = await getPublicKey(client, secretSync);
|
const publicKey = await getPublicKey(client, secretSync);
|
||||||
|
|
||||||
for await (const encryptedSecret of encryptedSecrets) {
|
|
||||||
if (!(encryptedSecret.name in secretMap)) {
|
|
||||||
await deleteSecret(client, secretSync, encryptedSecret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await sodium.ready.then(async () => {
|
await sodium.ready.then(async () => {
|
||||||
for await (const key of Object.keys(secretMap)) {
|
for await (const key of Object.keys(secretMap)) {
|
||||||
// convert secret & base64 key to Uint8Array.
|
// convert secret & base64 key to Uint8Array.
|
||||||
@@ -224,6 +218,14 @@ export const GithubSyncFns = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||||
|
|
||||||
|
for await (const encryptedSecret of encryptedSecrets) {
|
||||||
|
if (!(encryptedSecret.name in secretMap)) {
|
||||||
|
await deleteSecret(client, secretSync, encryptedSecret);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
getSecrets: async (secretSync: TGitHubSyncWithCredentials) => {
|
getSecrets: async (secretSync: TGitHubSyncWithCredentials) => {
|
||||||
throw new Error(`${SECRET_SYNC_NAME_MAP[secretSync.destination]} does not support importing secrets.`);
|
throw new Error(`${SECRET_SYNC_NAME_MAP[secretSync.destination]} does not support importing secrets.`);
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||||
|
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||||
|
import { TSecretSyncListItem } from "@app/services/secret-sync/secret-sync-types";
|
||||||
|
|
||||||
|
export const HUMANITEC_SYNC_LIST_OPTION: TSecretSyncListItem = {
|
||||||
|
name: "Humanitec",
|
||||||
|
destination: SecretSync.Humanitec,
|
||||||
|
connection: AppConnection.Humanitec,
|
||||||
|
canImportSecrets: false
|
||||||
|
};
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export enum HumanitecSyncScope {
|
||||||
|
Application = "application",
|
||||||
|
Environment = "environment"
|
||||||
|
}
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
import { request } from "@app/lib/config/request";
|
||||||
|
import { logger } from "@app/lib/logger";
|
||||||
|
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||||
|
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
||||||
|
import { SECRET_SYNC_NAME_MAP } from "@app/services/secret-sync/secret-sync-maps";
|
||||||
|
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||||
|
|
||||||
|
import { HumanitecSyncScope } from "./humanitec-sync-enums";
|
||||||
|
import { HumanitecSecret, THumanitecSyncWithCredentials } from "./humanitec-sync-types";
|
||||||
|
|
||||||
|
const getHumanitecSecrets = async (secretSync: THumanitecSyncWithCredentials) => {
|
||||||
|
const {
|
||||||
|
destinationConfig,
|
||||||
|
connection: {
|
||||||
|
credentials: { apiToken }
|
||||||
|
}
|
||||||
|
} = secretSync;
|
||||||
|
|
||||||
|
let url = `${IntegrationUrls.HUMANITEC_API_URL}/orgs/${destinationConfig.org}/apps/${destinationConfig.app}`;
|
||||||
|
if (destinationConfig.scope === HumanitecSyncScope.Environment) {
|
||||||
|
url += `/envs/${destinationConfig.env}`;
|
||||||
|
}
|
||||||
|
url += "/values";
|
||||||
|
|
||||||
|
const { data } = await request.get<HumanitecSecret[]>(url, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${apiToken}`,
|
||||||
|
Accept: "application/json"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteSecret = async (secretSync: THumanitecSyncWithCredentials, encryptedSecret: HumanitecSecret) => {
|
||||||
|
const {
|
||||||
|
destinationConfig,
|
||||||
|
connection: {
|
||||||
|
credentials: { apiToken }
|
||||||
|
}
|
||||||
|
} = secretSync;
|
||||||
|
|
||||||
|
if (destinationConfig.scope === HumanitecSyncScope.Environment && encryptedSecret.source === "app") {
|
||||||
|
logger.info(
|
||||||
|
`Humanitec secret ${encryptedSecret.key} on app ${destinationConfig.app} has no environment override, not deleted as it is an app-level secret`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let url = `${IntegrationUrls.HUMANITEC_API_URL}/orgs/${destinationConfig.org}/apps/${destinationConfig.app}`;
|
||||||
|
if (destinationConfig.scope === HumanitecSyncScope.Environment) {
|
||||||
|
url += `/envs/${destinationConfig.env}`;
|
||||||
|
}
|
||||||
|
url += `/values/${encryptedSecret.key}`;
|
||||||
|
|
||||||
|
await request.delete(url, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${apiToken}`,
|
||||||
|
Accept: "application/json"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
throw new SecretSyncError({
|
||||||
|
error,
|
||||||
|
secretKey: encryptedSecret.key
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const createSecret = async (secretSync: THumanitecSyncWithCredentials, secretMap: TSecretMap, key: string) => {
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
destinationConfig,
|
||||||
|
connection: {
|
||||||
|
credentials: { apiToken }
|
||||||
|
}
|
||||||
|
} = secretSync;
|
||||||
|
|
||||||
|
const appLevelSecret = destinationConfig.scope === HumanitecSyncScope.Application ? secretMap[key].value : "";
|
||||||
|
await request.post(
|
||||||
|
`${IntegrationUrls.HUMANITEC_API_URL}/orgs/${destinationConfig.org}/apps/${destinationConfig.app}/values`,
|
||||||
|
{
|
||||||
|
key,
|
||||||
|
value: appLevelSecret,
|
||||||
|
description: secretMap[key].comment || "",
|
||||||
|
is_secret: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${apiToken}`,
|
||||||
|
Accept: "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (destinationConfig.scope === HumanitecSyncScope.Environment) {
|
||||||
|
await request.patch(
|
||||||
|
`${IntegrationUrls.HUMANITEC_API_URL}/orgs/${destinationConfig.org}/apps/${destinationConfig.app}/envs/${destinationConfig.env}/values/${key}`,
|
||||||
|
{
|
||||||
|
value: secretMap[key].value,
|
||||||
|
description: secretMap[key].comment || ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${apiToken}`,
|
||||||
|
Accept: "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new SecretSyncError({
|
||||||
|
error,
|
||||||
|
secretKey: key
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateSecret = async (
|
||||||
|
secretSync: THumanitecSyncWithCredentials,
|
||||||
|
secretMap: TSecretMap,
|
||||||
|
encryptedSecret: HumanitecSecret
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
destinationConfig,
|
||||||
|
connection: {
|
||||||
|
credentials: { apiToken }
|
||||||
|
}
|
||||||
|
} = secretSync;
|
||||||
|
if (destinationConfig.scope === HumanitecSyncScope.Application) {
|
||||||
|
await request.patch(
|
||||||
|
`${IntegrationUrls.HUMANITEC_API_URL}/orgs/${destinationConfig.org}/apps/${destinationConfig.app}/values/${encryptedSecret.key}`,
|
||||||
|
{
|
||||||
|
value: secretMap[encryptedSecret.key].value,
|
||||||
|
description: secretMap[encryptedSecret.key].comment || ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${apiToken}`,
|
||||||
|
Accept: "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else if (encryptedSecret.source === "app") {
|
||||||
|
await request.post(
|
||||||
|
`${IntegrationUrls.HUMANITEC_API_URL}/orgs/${destinationConfig.org}/apps/${destinationConfig.app}/envs/${destinationConfig.env}/values`,
|
||||||
|
{
|
||||||
|
value: secretMap[encryptedSecret.key].value,
|
||||||
|
description: secretMap[encryptedSecret.key].comment || "",
|
||||||
|
key: encryptedSecret.key,
|
||||||
|
is_secret: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${apiToken}`,
|
||||||
|
Accept: "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await request.patch(
|
||||||
|
`${IntegrationUrls.HUMANITEC_API_URL}/orgs/${destinationConfig.org}/apps/${destinationConfig.app}/envs/${destinationConfig.env}/values/${encryptedSecret.key}`,
|
||||||
|
{
|
||||||
|
value: secretMap[encryptedSecret.key].value,
|
||||||
|
description: secretMap[encryptedSecret.key].comment || ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${apiToken}`,
|
||||||
|
Accept: "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new SecretSyncError({
|
||||||
|
error,
|
||||||
|
secretKey: encryptedSecret.key
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const HumanitecSyncFns = {
|
||||||
|
syncSecrets: async (secretSync: THumanitecSyncWithCredentials, secretMap: TSecretMap) => {
|
||||||
|
const humanitecSecrets = await getHumanitecSecrets(secretSync);
|
||||||
|
const humanitecSecretsKeys = new Map(humanitecSecrets.map((s) => [s.key, s]));
|
||||||
|
|
||||||
|
for await (const key of Object.keys(secretMap)) {
|
||||||
|
const existingSecret = humanitecSecretsKeys.get(key);
|
||||||
|
|
||||||
|
if (!existingSecret) {
|
||||||
|
await createSecret(secretSync, secretMap, key);
|
||||||
|
} else {
|
||||||
|
await updateSecret(secretSync, secretMap, existingSecret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||||
|
|
||||||
|
for await (const humanitecSecret of humanitecSecrets) {
|
||||||
|
if (!secretMap[humanitecSecret.key]) {
|
||||||
|
await deleteSecret(secretSync, humanitecSecret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getSecrets: async (secretSync: THumanitecSyncWithCredentials): Promise<TSecretMap> => {
|
||||||
|
throw new Error(`${SECRET_SYNC_NAME_MAP[secretSync.destination]} does not support importing secrets.`);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeSecrets: async (secretSync: THumanitecSyncWithCredentials, secretMap: TSecretMap) => {
|
||||||
|
const encryptedSecrets = await getHumanitecSecrets(secretSync);
|
||||||
|
|
||||||
|
for await (const encryptedSecret of encryptedSecrets) {
|
||||||
|
if (encryptedSecret.key in secretMap) {
|
||||||
|
await deleteSecret(secretSync, encryptedSecret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { SecretSyncs } from "@app/lib/api-docs";
|
||||||
|
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||||
|
import { HumanitecSyncScope } from "@app/services/secret-sync/humanitec/humanitec-sync-enums";
|
||||||
|
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||||
|
import {
|
||||||
|
BaseSecretSyncSchema,
|
||||||
|
GenericCreateSecretSyncFieldsSchema,
|
||||||
|
GenericUpdateSecretSyncFieldsSchema
|
||||||
|
} from "@app/services/secret-sync/secret-sync-schemas";
|
||||||
|
import { TSyncOptionsConfig } from "@app/services/secret-sync/secret-sync-types";
|
||||||
|
|
||||||
|
const HumanitecSyncDestinationConfigSchema = z.discriminatedUnion("scope", [
|
||||||
|
z.object({
|
||||||
|
scope: z.literal(HumanitecSyncScope.Application).describe(SecretSyncs.DESTINATION_CONFIG.HUMANITEC.scope),
|
||||||
|
org: z.string().min(1, "Org ID is required").describe(SecretSyncs.DESTINATION_CONFIG.HUMANITEC.org),
|
||||||
|
app: z.string().min(1, "App ID is required").describe(SecretSyncs.DESTINATION_CONFIG.HUMANITEC.app)
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
scope: z.literal(HumanitecSyncScope.Environment).describe(SecretSyncs.DESTINATION_CONFIG.HUMANITEC.scope),
|
||||||
|
org: z.string().min(1, "Org ID is required").describe(SecretSyncs.DESTINATION_CONFIG.HUMANITEC.org),
|
||||||
|
app: z.string().min(1, "App ID is required").describe(SecretSyncs.DESTINATION_CONFIG.HUMANITEC.app),
|
||||||
|
env: z.string().min(1, "Env ID is required").describe(SecretSyncs.DESTINATION_CONFIG.HUMANITEC.env)
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
|
||||||
|
const HumanitecSyncOptionsConfig: TSyncOptionsConfig = { canImportSecrets: false };
|
||||||
|
|
||||||
|
export const HumanitecSyncSchema = BaseSecretSyncSchema(SecretSync.Humanitec, HumanitecSyncOptionsConfig).extend({
|
||||||
|
destination: z.literal(SecretSync.Humanitec),
|
||||||
|
destinationConfig: HumanitecSyncDestinationConfigSchema
|
||||||
|
});
|
||||||
|
|
||||||
|
export const CreateHumanitecSyncSchema = GenericCreateSecretSyncFieldsSchema(
|
||||||
|
SecretSync.Humanitec,
|
||||||
|
HumanitecSyncOptionsConfig
|
||||||
|
).extend({
|
||||||
|
destinationConfig: HumanitecSyncDestinationConfigSchema
|
||||||
|
});
|
||||||
|
|
||||||
|
export const UpdateHumanitecSyncSchema = GenericUpdateSecretSyncFieldsSchema(
|
||||||
|
SecretSync.Humanitec,
|
||||||
|
HumanitecSyncOptionsConfig
|
||||||
|
).extend({
|
||||||
|
destinationConfig: HumanitecSyncDestinationConfigSchema.optional()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const HumanitecSyncListItemSchema = z.object({
|
||||||
|
name: z.literal("Humanitec"),
|
||||||
|
connection: z.literal(AppConnection.Humanitec),
|
||||||
|
destination: z.literal(SecretSync.Humanitec),
|
||||||
|
canImportSecrets: z.literal(false)
|
||||||
|
});
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import z from "zod";
|
||||||
|
|
||||||
|
import { THumanitecConnection } from "@app/services/app-connection/humanitec";
|
||||||
|
|
||||||
|
import { CreateHumanitecSyncSchema, HumanitecSyncListItemSchema, HumanitecSyncSchema } from "./humanitec-sync-schemas";
|
||||||
|
|
||||||
|
export type THumanitecSyncListItem = z.infer<typeof HumanitecSyncListItemSchema>;
|
||||||
|
|
||||||
|
export type THumanitecSync = z.infer<typeof HumanitecSyncSchema>;
|
||||||
|
|
||||||
|
export type THumanitecSyncInput = z.infer<typeof CreateHumanitecSyncSchema>;
|
||||||
|
|
||||||
|
export type THumanitecSyncWithCredentials = THumanitecSync & {
|
||||||
|
connection: THumanitecConnection;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type HumanitecSecret = {
|
||||||
|
description: string;
|
||||||
|
is_secret: boolean;
|
||||||
|
key: string;
|
||||||
|
source: "app" | "env";
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export * from "./humanitec-sync-constants";
|
||||||
|
export * from "./humanitec-sync-enums";
|
||||||
|
export * from "./humanitec-sync-fns";
|
||||||
|
export * from "./humanitec-sync-schemas";
|
||||||
|
export * from "./humanitec-sync-types";
|
||||||
@@ -5,7 +5,8 @@ export enum SecretSync {
|
|||||||
GCPSecretManager = "gcp-secret-manager",
|
GCPSecretManager = "gcp-secret-manager",
|
||||||
AzureKeyVault = "azure-key-vault",
|
AzureKeyVault = "azure-key-vault",
|
||||||
AzureAppConfiguration = "azure-app-configuration",
|
AzureAppConfiguration = "azure-app-configuration",
|
||||||
Databricks = "databricks"
|
Databricks = "databricks",
|
||||||
|
Humanitec = "humanitec"
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SecretSyncInitialSyncBehavior {
|
export enum SecretSyncInitialSyncBehavior {
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import { AZURE_APP_CONFIGURATION_SYNC_LIST_OPTION, azureAppConfigurationSyncFact
|
|||||||
import { AZURE_KEY_VAULT_SYNC_LIST_OPTION, azureKeyVaultSyncFactory } from "./azure-key-vault";
|
import { AZURE_KEY_VAULT_SYNC_LIST_OPTION, azureKeyVaultSyncFactory } from "./azure-key-vault";
|
||||||
import { GCP_SYNC_LIST_OPTION } from "./gcp";
|
import { GCP_SYNC_LIST_OPTION } from "./gcp";
|
||||||
import { GcpSyncFns } from "./gcp/gcp-sync-fns";
|
import { GcpSyncFns } from "./gcp/gcp-sync-fns";
|
||||||
|
import { HUMANITEC_SYNC_LIST_OPTION } from "./humanitec";
|
||||||
|
import { HumanitecSyncFns } from "./humanitec/humanitec-sync-fns";
|
||||||
|
|
||||||
const SECRET_SYNC_LIST_OPTIONS: Record<SecretSync, TSecretSyncListItem> = {
|
const SECRET_SYNC_LIST_OPTIONS: Record<SecretSync, TSecretSyncListItem> = {
|
||||||
[SecretSync.AWSParameterStore]: AWS_PARAMETER_STORE_SYNC_LIST_OPTION,
|
[SecretSync.AWSParameterStore]: AWS_PARAMETER_STORE_SYNC_LIST_OPTION,
|
||||||
@@ -32,7 +34,8 @@ const SECRET_SYNC_LIST_OPTIONS: Record<SecretSync, TSecretSyncListItem> = {
|
|||||||
[SecretSync.GCPSecretManager]: GCP_SYNC_LIST_OPTION,
|
[SecretSync.GCPSecretManager]: GCP_SYNC_LIST_OPTION,
|
||||||
[SecretSync.AzureKeyVault]: AZURE_KEY_VAULT_SYNC_LIST_OPTION,
|
[SecretSync.AzureKeyVault]: AZURE_KEY_VAULT_SYNC_LIST_OPTION,
|
||||||
[SecretSync.AzureAppConfiguration]: AZURE_APP_CONFIGURATION_SYNC_LIST_OPTION,
|
[SecretSync.AzureAppConfiguration]: AZURE_APP_CONFIGURATION_SYNC_LIST_OPTION,
|
||||||
[SecretSync.Databricks]: DATABRICKS_SYNC_LIST_OPTION
|
[SecretSync.Databricks]: DATABRICKS_SYNC_LIST_OPTION,
|
||||||
|
[SecretSync.Humanitec]: HUMANITEC_SYNC_LIST_OPTION
|
||||||
};
|
};
|
||||||
|
|
||||||
export const listSecretSyncOptions = () => {
|
export const listSecretSyncOptions = () => {
|
||||||
@@ -116,6 +119,8 @@ export const SecretSyncFns = {
|
|||||||
appConnectionDAL,
|
appConnectionDAL,
|
||||||
kmsService
|
kmsService
|
||||||
}).syncSecrets(secretSync, secretMap);
|
}).syncSecrets(secretSync, secretMap);
|
||||||
|
case SecretSync.Humanitec:
|
||||||
|
return HumanitecSyncFns.syncSecrets(secretSync, secretMap);
|
||||||
default:
|
default:
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unhandled sync destination for sync secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
`Unhandled sync destination for sync secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
||||||
@@ -157,6 +162,9 @@ export const SecretSyncFns = {
|
|||||||
appConnectionDAL,
|
appConnectionDAL,
|
||||||
kmsService
|
kmsService
|
||||||
}).getSecrets(secretSync);
|
}).getSecrets(secretSync);
|
||||||
|
case SecretSync.Humanitec:
|
||||||
|
secretMap = await HumanitecSyncFns.getSecrets(secretSync);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unhandled sync destination for get secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
`Unhandled sync destination for get secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
||||||
@@ -197,6 +205,8 @@ export const SecretSyncFns = {
|
|||||||
appConnectionDAL,
|
appConnectionDAL,
|
||||||
kmsService
|
kmsService
|
||||||
}).removeSecrets(secretSync, secretMap);
|
}).removeSecrets(secretSync, secretMap);
|
||||||
|
case SecretSync.Humanitec:
|
||||||
|
return HumanitecSyncFns.removeSecrets(secretSync, secretMap);
|
||||||
default:
|
default:
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unhandled sync destination for remove secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
`Unhandled sync destination for remove secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ export const SECRET_SYNC_NAME_MAP: Record<SecretSync, string> = {
|
|||||||
[SecretSync.GCPSecretManager]: "GCP Secret Manager",
|
[SecretSync.GCPSecretManager]: "GCP Secret Manager",
|
||||||
[SecretSync.AzureKeyVault]: "Azure Key Vault",
|
[SecretSync.AzureKeyVault]: "Azure Key Vault",
|
||||||
[SecretSync.AzureAppConfiguration]: "Azure App Configuration",
|
[SecretSync.AzureAppConfiguration]: "Azure App Configuration",
|
||||||
[SecretSync.Databricks]: "Databricks"
|
[SecretSync.Databricks]: "Databricks",
|
||||||
|
[SecretSync.Humanitec]: "Humanitec"
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
|
export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
|
||||||
@@ -18,5 +19,6 @@ export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
|
|||||||
[SecretSync.GCPSecretManager]: AppConnection.GCP,
|
[SecretSync.GCPSecretManager]: AppConnection.GCP,
|
||||||
[SecretSync.AzureKeyVault]: AppConnection.AzureKeyVault,
|
[SecretSync.AzureKeyVault]: AppConnection.AzureKeyVault,
|
||||||
[SecretSync.AzureAppConfiguration]: AppConnection.AzureAppConfiguration,
|
[SecretSync.AzureAppConfiguration]: AppConnection.AzureAppConfiguration,
|
||||||
[SecretSync.Databricks]: AppConnection.Databricks
|
[SecretSync.Databricks]: AppConnection.Databricks,
|
||||||
|
[SecretSync.Humanitec]: AppConnection.Humanitec
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ const BaseSyncOptionsSchema = <T extends AnyZodObject | undefined = undefined>({
|
|||||||
initialSyncBehavior: (canImportSecrets
|
initialSyncBehavior: (canImportSecrets
|
||||||
? z.nativeEnum(SecretSyncInitialSyncBehavior)
|
? z.nativeEnum(SecretSyncInitialSyncBehavior)
|
||||||
: z.literal(SecretSyncInitialSyncBehavior.OverwriteDestination)
|
: z.literal(SecretSyncInitialSyncBehavior.OverwriteDestination)
|
||||||
).describe(SecretSyncs.SYNC_OPTIONS(destination).initialSyncBehavior)
|
).describe(SecretSyncs.SYNC_OPTIONS(destination).initialSyncBehavior),
|
||||||
|
disableSecretDeletion: z.boolean().optional().describe(SecretSyncs.SYNC_OPTIONS(destination).disableSecretDeletion)
|
||||||
});
|
});
|
||||||
|
|
||||||
const schema = merge ? baseSchema.merge(merge) : baseSchema;
|
const schema = merge ? baseSchema.merge(merge) : baseSchema;
|
||||||
|
|||||||
@@ -43,6 +43,12 @@ import {
|
|||||||
TAzureKeyVaultSyncWithCredentials
|
TAzureKeyVaultSyncWithCredentials
|
||||||
} from "./azure-key-vault";
|
} from "./azure-key-vault";
|
||||||
import { TGcpSync, TGcpSyncInput, TGcpSyncListItem, TGcpSyncWithCredentials } from "./gcp";
|
import { TGcpSync, TGcpSyncInput, TGcpSyncListItem, TGcpSyncWithCredentials } from "./gcp";
|
||||||
|
import {
|
||||||
|
THumanitecSync,
|
||||||
|
THumanitecSyncInput,
|
||||||
|
THumanitecSyncListItem,
|
||||||
|
THumanitecSyncWithCredentials
|
||||||
|
} from "./humanitec";
|
||||||
|
|
||||||
export type TSecretSync =
|
export type TSecretSync =
|
||||||
| TAwsParameterStoreSync
|
| TAwsParameterStoreSync
|
||||||
@@ -51,7 +57,8 @@ export type TSecretSync =
|
|||||||
| TGcpSync
|
| TGcpSync
|
||||||
| TAzureKeyVaultSync
|
| TAzureKeyVaultSync
|
||||||
| TAzureAppConfigurationSync
|
| TAzureAppConfigurationSync
|
||||||
| TDatabricksSync;
|
| TDatabricksSync
|
||||||
|
| THumanitecSync;
|
||||||
|
|
||||||
export type TSecretSyncWithCredentials =
|
export type TSecretSyncWithCredentials =
|
||||||
| TAwsParameterStoreSyncWithCredentials
|
| TAwsParameterStoreSyncWithCredentials
|
||||||
@@ -60,7 +67,8 @@ export type TSecretSyncWithCredentials =
|
|||||||
| TGcpSyncWithCredentials
|
| TGcpSyncWithCredentials
|
||||||
| TAzureKeyVaultSyncWithCredentials
|
| TAzureKeyVaultSyncWithCredentials
|
||||||
| TAzureAppConfigurationSyncWithCredentials
|
| TAzureAppConfigurationSyncWithCredentials
|
||||||
| TDatabricksSyncWithCredentials;
|
| TDatabricksSyncWithCredentials
|
||||||
|
| THumanitecSyncWithCredentials;
|
||||||
|
|
||||||
export type TSecretSyncInput =
|
export type TSecretSyncInput =
|
||||||
| TAwsParameterStoreSyncInput
|
| TAwsParameterStoreSyncInput
|
||||||
@@ -69,7 +77,8 @@ export type TSecretSyncInput =
|
|||||||
| TGcpSyncInput
|
| TGcpSyncInput
|
||||||
| TAzureKeyVaultSyncInput
|
| TAzureKeyVaultSyncInput
|
||||||
| TAzureAppConfigurationSyncInput
|
| TAzureAppConfigurationSyncInput
|
||||||
| TDatabricksSyncInput;
|
| TDatabricksSyncInput
|
||||||
|
| THumanitecSyncInput;
|
||||||
|
|
||||||
export type TSecretSyncListItem =
|
export type TSecretSyncListItem =
|
||||||
| TAwsParameterStoreSyncListItem
|
| TAwsParameterStoreSyncListItem
|
||||||
@@ -78,7 +87,8 @@ export type TSecretSyncListItem =
|
|||||||
| TGcpSyncListItem
|
| TGcpSyncListItem
|
||||||
| TAzureKeyVaultSyncListItem
|
| TAzureKeyVaultSyncListItem
|
||||||
| TAzureAppConfigurationSyncListItem
|
| TAzureAppConfigurationSyncListItem
|
||||||
| TDatabricksSyncListItem;
|
| TDatabricksSyncListItem
|
||||||
|
| THumanitecSyncListItem;
|
||||||
|
|
||||||
export type TSyncOptionsConfig = {
|
export type TSyncOptionsConfig = {
|
||||||
canImportSecrets: boolean;
|
canImportSecrets: boolean;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { getConfig } from "@app/lib/config/env";
|
|||||||
import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
|
import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
|
||||||
import { getUserPrivateKey } from "@app/lib/crypto/srp";
|
import { getUserPrivateKey } from "@app/lib/crypto/srp";
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
|
import { TIdentityDALFactory } from "@app/services/identity/identity-dal";
|
||||||
|
|
||||||
import { TAuthLoginFactory } from "../auth/auth-login-service";
|
import { TAuthLoginFactory } from "../auth/auth-login-service";
|
||||||
import { AuthMethod } from "../auth/auth-type";
|
import { AuthMethod } from "../auth/auth-type";
|
||||||
@@ -20,7 +21,6 @@ import { TUserAliasDALFactory } from "../user-alias/user-alias-dal";
|
|||||||
import { UserAliasType } from "../user-alias/user-alias-types";
|
import { UserAliasType } from "../user-alias/user-alias-types";
|
||||||
import { TSuperAdminDALFactory } from "./super-admin-dal";
|
import { TSuperAdminDALFactory } from "./super-admin-dal";
|
||||||
import { LoginMethod, TAdminGetIdentitiesDTO, TAdminGetUsersDTO, TAdminSignUpDTO } from "./super-admin-types";
|
import { LoginMethod, TAdminGetIdentitiesDTO, TAdminGetUsersDTO, TAdminSignUpDTO } from "./super-admin-types";
|
||||||
import { TIdentityDALFactory } from "@app/services/identity/identity-dal";
|
|
||||||
|
|
||||||
type TSuperAdminServiceFactoryDep = {
|
type TSuperAdminServiceFactoryDep = {
|
||||||
identityDAL: Pick<TIdentityDALFactory, "getIdentitiesByFilter">;
|
identityDAL: Pick<TIdentityDALFactory, "getIdentitiesByFilter">;
|
||||||
|
|||||||
@@ -143,7 +143,15 @@ var secretsSetCmd = &cobra.Command{
|
|||||||
Short: "Used set secrets",
|
Short: "Used set secrets",
|
||||||
Use: "set [secrets]",
|
Use: "set [secrets]",
|
||||||
DisableFlagsInUseLine: true,
|
DisableFlagsInUseLine: true,
|
||||||
Args: cobra.MinimumNArgs(1),
|
Args: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if cmd.Flags().Changed("file") {
|
||||||
|
if len(args) > 0 {
|
||||||
|
return fmt.Errorf("secrets cannot be provided as command-line arguments when the --file option is used. Please choose either file-based or argument-based secret input")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return cobra.MinimumNArgs(1)(cmd, args)
|
||||||
|
},
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
token, err := util.GetInfisicalToken(cmd)
|
token, err := util.GetInfisicalToken(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -177,13 +185,18 @@ var secretsSetCmd = &cobra.Command{
|
|||||||
util.HandleError(err, "Unable to parse secret type")
|
util.HandleError(err, "Unable to parse secret type")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
file, err := cmd.Flags().GetString("file")
|
||||||
|
if err != nil {
|
||||||
|
util.HandleError(err, "Unable to parse flag")
|
||||||
|
}
|
||||||
|
|
||||||
var secretOperations []models.SecretSetOperation
|
var secretOperations []models.SecretSetOperation
|
||||||
if token != nil && (token.Type == util.SERVICE_TOKEN_IDENTIFIER || token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER) {
|
if token != nil && (token.Type == util.SERVICE_TOKEN_IDENTIFIER || token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER) {
|
||||||
if projectId == "" {
|
if projectId == "" {
|
||||||
util.PrintErrorMessageAndExit("When using service tokens or machine identities, you must set the --projectId flag")
|
util.PrintErrorMessageAndExit("When using service tokens or machine identities, you must set the --projectId flag")
|
||||||
}
|
}
|
||||||
|
|
||||||
secretOperations, err = util.SetRawSecrets(args, secretType, environmentName, secretsPath, projectId, token)
|
secretOperations, err = util.SetRawSecrets(args, secretType, environmentName, secretsPath, projectId, token, file)
|
||||||
} else {
|
} else {
|
||||||
if projectId == "" {
|
if projectId == "" {
|
||||||
workspaceFile, err := util.GetWorkSpaceFromFile()
|
workspaceFile, err := util.GetWorkSpaceFromFile()
|
||||||
@@ -206,7 +219,7 @@ var secretsSetCmd = &cobra.Command{
|
|||||||
secretOperations, err = util.SetRawSecrets(args, secretType, environmentName, secretsPath, projectId, &models.TokenDetails{
|
secretOperations, err = util.SetRawSecrets(args, secretType, environmentName, secretsPath, projectId, &models.TokenDetails{
|
||||||
Type: "",
|
Type: "",
|
||||||
Token: loggedInUserDetails.UserCredentials.JTWToken,
|
Token: loggedInUserDetails.UserCredentials.JTWToken,
|
||||||
})
|
}, file)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -691,6 +704,7 @@ func init() {
|
|||||||
secretsSetCmd.Flags().String("projectId", "", "manually set the project ID to for setting secrets when using machine identity based auth")
|
secretsSetCmd.Flags().String("projectId", "", "manually set the project ID to for setting secrets when using machine identity based auth")
|
||||||
secretsSetCmd.Flags().String("path", "/", "set secrets within a folder path")
|
secretsSetCmd.Flags().String("path", "/", "set secrets within a folder path")
|
||||||
secretsSetCmd.Flags().String("type", util.SECRET_TYPE_SHARED, "the type of secret to create: personal or shared")
|
secretsSetCmd.Flags().String("type", util.SECRET_TYPE_SHARED, "the type of secret to create: personal or shared")
|
||||||
|
secretsSetCmd.Flags().String("file", "", "Load secrets from the specified file. File format: .env or YAML (comments: # or //). This option is mutually exclusive with command-line secrets arguments.")
|
||||||
|
|
||||||
secretsDeleteCmd.Flags().String("type", "personal", "the type of secret to delete: personal or shared (default: personal)")
|
secretsDeleteCmd.Flags().String("type", "personal", "the type of secret to delete: personal or shared (default: personal)")
|
||||||
secretsDeleteCmd.Flags().String("token", "", "Fetch secrets using service token or machine identity access token")
|
secretsDeleteCmd.Flags().String("token", "", "Fetch secrets using service token or machine identity access token")
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/zalando/go-keyring"
|
"github.com/zalando/go-keyring"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetPlainTextSecretsViaServiceToken(fullServiceToken string, environment string, secretPath string, includeImports bool, recursive bool, tagSlugs string, expandSecretReferences bool) ([]models.SingleEnvironmentVariable, error) {
|
func GetPlainTextSecretsViaServiceToken(fullServiceToken string, environment string, secretPath string, includeImports bool, recursive bool, tagSlugs string, expandSecretReferences bool) ([]models.SingleEnvironmentVariable, error) {
|
||||||
@@ -564,7 +565,99 @@ func GetPlainTextWorkspaceKey(authenticationToken string, receiverPrivateKey str
|
|||||||
return crypto.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey), nil
|
return crypto.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetRawSecrets(secretArgs []string, secretType string, environmentName string, secretsPath string, projectId string, tokenDetails *models.TokenDetails) ([]models.SecretSetOperation, error) {
|
func parseSecrets(fileName string, content string) (map[string]string, error) {
|
||||||
|
secrets := make(map[string]string)
|
||||||
|
|
||||||
|
if strings.HasSuffix(fileName, ".yaml") || strings.HasSuffix(fileName, ".yml") {
|
||||||
|
// Handle YAML secrets
|
||||||
|
var yamlData map[string]interface{}
|
||||||
|
if err := yaml.Unmarshal([]byte(content), &yamlData); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse YAML file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range yamlData {
|
||||||
|
if strValue, ok := value.(string); ok {
|
||||||
|
secrets[key] = strValue
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("YAML secret '%s' must be a string", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Handle .env files
|
||||||
|
lines := strings.Split(content, "\n")
|
||||||
|
|
||||||
|
for _, line := range lines {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
|
||||||
|
// Ignore empty lines and comments
|
||||||
|
if line == "" || strings.HasPrefix(line, "#") || strings.HasPrefix(line, "//") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure it's a valid key=value pair
|
||||||
|
splitKeyValue := strings.SplitN(line, "=", 2)
|
||||||
|
if len(splitKeyValue) != 2 {
|
||||||
|
return nil, fmt.Errorf("invalid format, expected key=value in line: %s", line)
|
||||||
|
}
|
||||||
|
|
||||||
|
key, value := strings.TrimSpace(splitKeyValue[0]), strings.TrimSpace(splitKeyValue[1])
|
||||||
|
|
||||||
|
// Handle quoted values
|
||||||
|
if (strings.HasPrefix(value, `"`) && strings.HasSuffix(value, `"`)) ||
|
||||||
|
(strings.HasPrefix(value, `'`) && strings.HasSuffix(value, `'`)) {
|
||||||
|
value = value[1 : len(value)-1] // Remove surrounding quotes
|
||||||
|
}
|
||||||
|
|
||||||
|
secrets[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return secrets, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateSecretKey(key string) error {
|
||||||
|
if key == "" {
|
||||||
|
return errors.New("secret keys cannot be empty")
|
||||||
|
}
|
||||||
|
if unicode.IsNumber(rune(key[0])) {
|
||||||
|
return fmt.Errorf("secret key '%s' cannot start with a number", key)
|
||||||
|
}
|
||||||
|
if strings.Contains(key, " ") {
|
||||||
|
return fmt.Errorf("secret key '%s' cannot contain spaces", key)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetRawSecrets(secretArgs []string, secretType string, environmentName string, secretsPath string, projectId string, tokenDetails *models.TokenDetails, file string) ([]models.SecretSetOperation, error) {
|
||||||
|
if file != "" {
|
||||||
|
content, err := os.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
PrintErrorMessageAndExit("File does not exist")
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unable to process file [err=%v]", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedSecrets, err := parseSecrets(file, string(content))
|
||||||
|
if err != nil {
|
||||||
|
PrintErrorMessageAndExit(fmt.Sprintf("error parsing secrets: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Validate secrets
|
||||||
|
for key, value := range parsedSecrets {
|
||||||
|
if err := validateSecretKey(key); err != nil {
|
||||||
|
PrintErrorMessageAndExit(err.Error())
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(value) == "" {
|
||||||
|
PrintErrorMessageAndExit(fmt.Sprintf("Secret key '%s' has an empty value", key))
|
||||||
|
}
|
||||||
|
secretArgs = append(secretArgs, fmt.Sprintf("%s=%s", key, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(secretArgs) == 0 {
|
||||||
|
PrintErrorMessageAndExit("no valid secrets found in the file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if tokenDetails == nil {
|
if tokenDetails == nil {
|
||||||
return nil, fmt.Errorf("unable to process set secret operations, token details are missing")
|
return nil, fmt.Errorf("unable to process set secret operations, token details are missing")
|
||||||
|
|||||||
@@ -15,15 +15,3 @@ Since Infisical's team is globally distributed, it is hard for us to keep track
|
|||||||
## Winter break
|
## Winter break
|
||||||
|
|
||||||
Every year, Infisical team goes on a company-wide vacation during winter holidays. This year, the winter break period starts on December 21st, 2024 and ends on January 5th, 2025. You should expect to do no scheduled work during this period, but we will have a rotation process for [high and urgent service disruptions](https://infisical.com/sla).
|
Every year, Infisical team goes on a company-wide vacation during winter holidays. This year, the winter break period starts on December 21st, 2024 and ends on January 5th, 2025. You should expect to do no scheduled work during this period, but we will have a rotation process for [high and urgent service disruptions](https://infisical.com/sla).
|
||||||
|
|
||||||
## Parental leave
|
|
||||||
|
|
||||||
At Infisical, we recognize that parental leave is a special and important time, significantly different from a typical vacation. We’re proud to offer parental leave to everyone, regardless of gender, and whether you’ve become a parent through childbirth or adoption.
|
|
||||||
|
|
||||||
For team members who have been with Infisical for over a year by the time of your child’s birth or adoption, you are eligible for up to 12 weeks of paid parental leave. This leave will be provided in one continuous block to allow you uninterrupted time with your family. If you have been with Infisical for less than a year, we will follow the parental leave provisions required by your local jurisdiction.
|
|
||||||
|
|
||||||
While we trust your judgment, parental leave is intended to be a distinct benefit and is not designed to be combined with our unlimited PTO policy. To ensure fairness and balance, we generally discourage combining parental leave with an extended vacation.
|
|
||||||
|
|
||||||
When you’re ready, please notify Maidul about your plans for parental leave, ideally at least four months in advance. This allows us to support you fully and arrange any necessary logistics, including salary adjustments and statutory paperwork.
|
|
||||||
|
|
||||||
We’re here to support you as you embark on this exciting new chapter in your life!
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
title: "Available"
|
||||||
|
openapi: "GET /api/v1/app-connections/humanitec/available"
|
||||||
|
---
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
title: "Create"
|
||||||
|
openapi: "POST /api/v1/app-connections/humanitec"
|
||||||
|
---
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user