mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat(infisical-pg): completed audit log moved ee routes to normal routing pattern
This commit is contained in:
@@ -1,89 +0,0 @@
|
||||
import dotenv from "dotenv";
|
||||
|
||||
import { initDbConnection } from "./src/db";
|
||||
import { TableName } from "./src/db/schemas";
|
||||
import { selectAllTableCols } from "./src/lib/knex";
|
||||
import picomatch from "picomatch";
|
||||
|
||||
dotenv.config();
|
||||
// const db = initDbConnection(process.env.DB_CONNECTION_URI);
|
||||
|
||||
// const main = async () => {
|
||||
// const folders = db
|
||||
// .withRecursive("parent", (qb) => {
|
||||
// qb.select({
|
||||
// depth: 1,
|
||||
// path: db.raw("'/'")
|
||||
// })
|
||||
// .select(selectAllTableCols(db, TableName.SecretFolder))
|
||||
// .from(TableName.SecretFolder)
|
||||
// .join(
|
||||
// TableName.Environment,
|
||||
// `${TableName.SecretFolder}.envId`,
|
||||
// `${TableName.Environment}.id`
|
||||
// )
|
||||
// .where({
|
||||
// projectId: "01c10de1-8743-490f-9c8a-7a19c4dc72a9",
|
||||
// parentId: null
|
||||
// })
|
||||
// .where(`${TableName.Environment}.slug`, "dev")
|
||||
// .union((qb) =>
|
||||
// qb
|
||||
// .select({
|
||||
// depth: db.raw("parent.depth + 1"),
|
||||
// path: db.raw(
|
||||
// "CONCAT((CASE WHEN parent.path = '/' THEN '' ELSE parent.path END),'/', secret_folders.name)"
|
||||
// )
|
||||
// })
|
||||
// .select(selectAllTableCols(db, TableName.SecretFolder))
|
||||
// .whereRaw(
|
||||
// `depth = array_position(ARRAY[${[1, 2]
|
||||
// .map((_) => "?")
|
||||
// .join(",")}]::varchar[], secret_folders.name,depth)`,
|
||||
// [...["ui", "design"]]
|
||||
// )
|
||||
// .from(TableName.SecretFolder)
|
||||
// .join("parent", "parent.id", `${TableName.SecretFolder}.parentId`)
|
||||
// );
|
||||
// })
|
||||
// .select("*")
|
||||
// .from("parent")
|
||||
// .orderBy("depth", "desc")
|
||||
// .first();
|
||||
// console.log(folders.toSQL());
|
||||
// console.log(JSON.stringify(await folders, null, 4));
|
||||
// process.exit(0);
|
||||
// };
|
||||
//
|
||||
const main = async () => {
|
||||
const isMatch = picomatch("/ui/**/seomthing", { strictSlashes: false });
|
||||
console.log(
|
||||
picomatch.isMatch(
|
||||
"/ui/(base|new)/seomthing",
|
||||
["/ui/base/seomthing", "/ui/(base|new)/seomthing"],
|
||||
{
|
||||
strictSlashes: false
|
||||
}
|
||||
)
|
||||
);
|
||||
// console.log(isMatch("/ui/(base|new)/seomthing"));
|
||||
// const folders = db(TableName.SecretVersion)
|
||||
// .whereIn(`${TableName.SecretVersion}.secretId`, ["8fe1c5e2-af6a-40ed-874b-6738816236bc"])
|
||||
// .join(
|
||||
// db(TableName.SecretVersion)
|
||||
// .groupBy("secretId")
|
||||
// .max("version")
|
||||
// .select("secretId")
|
||||
// .as("latestVersion"),
|
||||
// (bd) => {
|
||||
// bd.on(`${TableName.SecretVersion}.secretId`, "latestVersion.secretId").andOn(
|
||||
// `${TableName.SecretVersion}.version`,
|
||||
// "latestVersion.max"
|
||||
// );
|
||||
// }
|
||||
// );
|
||||
// console.log(folders.toSQL());
|
||||
// console.log(JSON.stringify(await folders, null, 4));
|
||||
// process.exit(0);
|
||||
};
|
||||
main();
|
||||
241
backend-pg/package-lock.json
generated
241
backend-pg/package-lock.json
generated
@@ -18,6 +18,7 @@
|
||||
"@fastify/session": "^10.7.0",
|
||||
"@fastify/swagger": "^8.12.0",
|
||||
"@fastify/swagger-ui": "^1.10.1",
|
||||
"@node-saml/passport-saml": "^4.0.4",
|
||||
"@octokit/rest": "^20.0.2",
|
||||
"@ucast/mongo2js": "^1.3.4",
|
||||
"ajv": "^8.12.0",
|
||||
@@ -996,6 +997,64 @@
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@node-saml/node-saml": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@node-saml/node-saml/-/node-saml-4.0.5.tgz",
|
||||
"integrity": "sha512-J5DglElbY1tjOuaR1NPtjOXkXY5bpUhDoKVoeucYN98A3w4fwgjIOPqIGcb6cQsqFq2zZ6vTCeKn5C/hvefSaw==",
|
||||
"dependencies": {
|
||||
"@types/debug": "^4.1.7",
|
||||
"@types/passport": "^1.0.11",
|
||||
"@types/xml-crypto": "^1.4.2",
|
||||
"@types/xml-encryption": "^1.2.1",
|
||||
"@types/xml2js": "^0.4.11",
|
||||
"@xmldom/xmldom": "^0.8.6",
|
||||
"debug": "^4.3.4",
|
||||
"xml-crypto": "^3.0.1",
|
||||
"xml-encryption": "^3.0.2",
|
||||
"xml2js": "^0.5.0",
|
||||
"xmlbuilder": "^15.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/@node-saml/node-saml/node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@node-saml/node-saml/node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/@node-saml/passport-saml": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@node-saml/passport-saml/-/passport-saml-4.0.4.tgz",
|
||||
"integrity": "sha512-xFw3gw0yo+K1mzlkW15NeBF7cVpRHN/4vpjmBKzov5YFImCWh/G0LcTZ8krH3yk2/eRPc3Or8LRPudVJBjmYaw==",
|
||||
"dependencies": {
|
||||
"@node-saml/node-saml": "^4.0.4",
|
||||
"@types/express": "^4.17.14",
|
||||
"@types/passport": "^1.0.11",
|
||||
"@types/passport-strategy": "^0.2.35",
|
||||
"passport": "^0.6.0",
|
||||
"passport-strategy": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -1640,7 +1699,6 @@
|
||||
"version": "1.19.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
|
||||
"integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/connect": "*",
|
||||
"@types/node": "*"
|
||||
@@ -1650,16 +1708,22 @@
|
||||
"version": "3.4.38",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
|
||||
"integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/debug": {
|
||||
"version": "4.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
|
||||
"integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
|
||||
"dependencies": {
|
||||
"@types/ms": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/express": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
|
||||
"integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/body-parser": "*",
|
||||
"@types/express-serve-static-core": "^4.17.33",
|
||||
@@ -1671,7 +1735,6 @@
|
||||
"version": "4.17.41",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz",
|
||||
"integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"@types/qs": "*",
|
||||
@@ -1682,8 +1745,7 @@
|
||||
"node_modules/@types/http-errors": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz",
|
||||
"integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA=="
|
||||
},
|
||||
"node_modules/@types/jmespath": {
|
||||
"version": "0.15.2",
|
||||
@@ -1719,14 +1781,17 @@
|
||||
"node_modules/@types/mime": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
|
||||
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
|
||||
"dev": true
|
||||
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="
|
||||
},
|
||||
"node_modules/@types/ms": {
|
||||
"version": "0.7.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz",
|
||||
"integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.9.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.5.tgz",
|
||||
"integrity": "sha512-Uq2xbNq0chGg+/WQEU0LJTSs/1nKxz6u1iemLcGomkSnKokbW1fbLqc3HOqCf2JP7KjlL4QkS7oZZTrOQHQYgQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
@@ -1753,7 +1818,6 @@
|
||||
"version": "1.0.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.16.tgz",
|
||||
"integrity": "sha512-FD0qD5hbPWQzaM0wHUnJ/T0BBCJBxCeemtnCwc/ThhTg3x9jfrAcRUmj5Dopza+MfFS9acTe3wk7rcVnRIp/0A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/express": "*"
|
||||
}
|
||||
@@ -1791,6 +1855,15 @@
|
||||
"@types/passport": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/passport-strategy": {
|
||||
"version": "0.2.38",
|
||||
"resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.38.tgz",
|
||||
"integrity": "sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==",
|
||||
"dependencies": {
|
||||
"@types/express": "*",
|
||||
"@types/passport": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/pg": {
|
||||
"version": "8.10.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.10.9.tgz",
|
||||
@@ -1874,14 +1947,12 @@
|
||||
"node_modules/@types/qs": {
|
||||
"version": "6.9.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz",
|
||||
"integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw=="
|
||||
},
|
||||
"node_modules/@types/range-parser": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
|
||||
"integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="
|
||||
},
|
||||
"node_modules/@types/semver": {
|
||||
"version": "7.5.6",
|
||||
@@ -1892,7 +1963,6 @@
|
||||
"version": "0.17.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
|
||||
"integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/mime": "^1",
|
||||
"@types/node": "*"
|
||||
@@ -1902,13 +1972,37 @@
|
||||
"version": "1.15.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz",
|
||||
"integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/http-errors": "*",
|
||||
"@types/mime": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/xml-encryption/-/xml-encryption-1.2.4.tgz",
|
||||
"integrity": "sha512-I69K/WW1Dv7j6O3jh13z0X8sLWJRXbu5xnHDl9yHzUNDUBtUoBY058eb5s+x/WG6yZC1h8aKdI2EoyEPjyEh+Q==",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/xml2js": {
|
||||
"version": "0.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.14.tgz",
|
||||
"integrity": "sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "6.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.2.tgz",
|
||||
@@ -2305,6 +2399,14 @@
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@xmldom/xmldom": {
|
||||
"version": "0.8.10",
|
||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
|
||||
"integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/abbrev": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||
@@ -6699,6 +6801,23 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/passport": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz",
|
||||
"integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==",
|
||||
"dependencies": {
|
||||
"passport-strategy": "1.x.x",
|
||||
"pause": "0.0.1",
|
||||
"utils-merge": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jaredhanson"
|
||||
}
|
||||
},
|
||||
"node_modules/passport-github": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-github/-/passport-github-1.1.0.tgz",
|
||||
@@ -6836,6 +6955,11 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/pause": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
|
||||
"integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
|
||||
},
|
||||
"node_modules/pg": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz",
|
||||
@@ -7781,6 +7905,11 @@
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"node_modules/sax": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz",
|
||||
"integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA=="
|
||||
},
|
||||
"node_modules/secure-json-parse": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz",
|
||||
@@ -9295,8 +9424,7 @@
|
||||
"node_modules/undici-types": {
|
||||
"version": "5.26.5",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
|
||||
},
|
||||
"node_modules/universal-user-agent": {
|
||||
"version": "6.0.1",
|
||||
@@ -10297,6 +10425,83 @@
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
},
|
||||
"node_modules/xml-crypto": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/xml-crypto/-/xml-crypto-3.2.0.tgz",
|
||||
"integrity": "sha512-qVurBUOQrmvlgmZqIVBqmb06TD2a/PpEUfFPgD7BuBfjmoH4zgkqaWSIJrnymlCvM2GGt9x+XtJFA+ttoAufqg==",
|
||||
"dependencies": {
|
||||
"@xmldom/xmldom": "^0.8.8",
|
||||
"xpath": "0.0.32"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xml-crypto/node_modules/xpath": {
|
||||
"version": "0.0.32",
|
||||
"resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.32.tgz",
|
||||
"integrity": "sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw==",
|
||||
"engines": {
|
||||
"node": ">=0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xml-encryption": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xml-encryption/-/xml-encryption-3.0.2.tgz",
|
||||
"integrity": "sha512-VxYXPvsWB01/aqVLd6ZMPWZ+qaj0aIdF+cStrVJMcFj3iymwZeI0ABzB3VqMYv48DkSpRhnrXqTUkR34j+UDyg==",
|
||||
"dependencies": {
|
||||
"@xmldom/xmldom": "^0.8.5",
|
||||
"escape-html": "^1.0.3",
|
||||
"xpath": "0.0.32"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/xml-encryption/node_modules/xpath": {
|
||||
"version": "0.0.32",
|
||||
"resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.32.tgz",
|
||||
"integrity": "sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw==",
|
||||
"engines": {
|
||||
"node": ">=0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xml2js": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
|
||||
"integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==",
|
||||
"dependencies": {
|
||||
"sax": ">=0.6.0",
|
||||
"xmlbuilder": "~11.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xmlbuilder": {
|
||||
"version": "15.1.1",
|
||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz",
|
||||
"integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==",
|
||||
"engines": {
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xpath": {
|
||||
"version": "0.0.27",
|
||||
"resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.27.tgz",
|
||||
"integrity": "sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ==",
|
||||
"engines": {
|
||||
"node": ">=0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"@fastify/session": "^10.7.0",
|
||||
"@fastify/swagger": "^8.12.0",
|
||||
"@fastify/swagger-ui": "^1.10.1",
|
||||
"@node-saml/passport-saml": "^4.0.4",
|
||||
"@octokit/rest": "^20.0.2",
|
||||
"@ucast/mongo2js": "^1.3.4",
|
||||
"ajv": "^8.12.0",
|
||||
|
||||
7
backend-pg/src/@types/fastify.d.ts
vendored
7
backend-pg/src/@types/fastify.d.ts
vendored
@@ -1,7 +1,10 @@
|
||||
import "fastify";
|
||||
|
||||
import { TUsers } from "@app/db/schemas";
|
||||
import { TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-service";
|
||||
import { TCreateAuditLogDTO } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { TSamlConfigServiceFactory } from "@app/ee/services/saml-config/saml-config-service";
|
||||
import { TSecretApprovalPolicyServiceFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-service";
|
||||
import { TSecretApprovalRequestServiceFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-service";
|
||||
import { TSecretRotationServiceFactory } from "@app/ee/services/secret-rotation/secret-rotation-service";
|
||||
@@ -62,6 +65,8 @@ declare module "fastify" {
|
||||
isCompleted: string;
|
||||
providerAuthToken: string;
|
||||
};
|
||||
auditLogInfo: Pick<TCreateAuditLogDTO, "userAgent" | "userAgentType" | "ipAddress" | "actor">;
|
||||
ssoConfig: Awaited<ReturnType<TSamlConfigServiceFactory["getSaml"]>>;
|
||||
}
|
||||
|
||||
interface FastifyInstance {
|
||||
@@ -98,6 +103,8 @@ declare module "fastify" {
|
||||
secretApprovalRequest: TSecretApprovalRequestServiceFactory;
|
||||
secretRotation: TSecretRotationServiceFactory;
|
||||
snapshot: TSecretSnapshotServiceFactory;
|
||||
saml: TSamlConfigServiceFactory;
|
||||
auditLog: TAuditLogServiceFactory;
|
||||
};
|
||||
|
||||
// this is exclusive use for middlewares in which we need to inject data
|
||||
|
||||
16
backend-pg/src/@types/knex.d.ts
vendored
16
backend-pg/src/@types/knex.d.ts
vendored
@@ -5,6 +5,9 @@ import {
|
||||
TApiKeys,
|
||||
TApiKeysInsert,
|
||||
TApiKeysUpdate,
|
||||
TAuditLogs,
|
||||
TAuditLogsInsert,
|
||||
TAuditLogsUpdate,
|
||||
TAuthTokens,
|
||||
TAuthTokenSessions,
|
||||
TAuthTokenSessionsInsert,
|
||||
@@ -43,6 +46,9 @@ import {
|
||||
TOrganizations,
|
||||
TOrganizationsInsert,
|
||||
TOrganizationsUpdate,
|
||||
TOrgBots,
|
||||
TOrgBotsInsert,
|
||||
TOrgBotsUpdate,
|
||||
TOrgMemberships,
|
||||
TOrgMembershipsInsert,
|
||||
TOrgMembershipsUpdate,
|
||||
@@ -67,6 +73,9 @@ import {
|
||||
TProjects,
|
||||
TProjectsInsert,
|
||||
TProjectsUpdate,
|
||||
TSamlConfigs,
|
||||
TSamlConfigsInsert,
|
||||
TSamlConfigsUpdate,
|
||||
TSapApprovers,
|
||||
TSapApproversInsert,
|
||||
TSapApproversUpdate,
|
||||
@@ -357,6 +366,13 @@ declare module "knex/types/tables" {
|
||||
TSecretSnapshotFoldersInsert,
|
||||
TSecretSnapshotFoldersUpdate
|
||||
>;
|
||||
[TableName.SamlConfig]: Knex.CompositeTableType<
|
||||
TSamlConfigs,
|
||||
TSamlConfigsInsert,
|
||||
TSamlConfigsUpdate
|
||||
>;
|
||||
[TableName.OrgBot]: Knex.CompositeTableType<TOrgBots, TOrgBotsInsert, TOrgBotsUpdate>;
|
||||
[TableName.AuditLog]: Knex.CompositeTableType<TAuditLogs, TAuditLogsInsert, TAuditLogsUpdate>;
|
||||
// Junction tables
|
||||
[TableName.JnSecretTag]: Knex.CompositeTableType<
|
||||
TSecretTagJunction,
|
||||
|
||||
@@ -22,7 +22,7 @@ export async function up(knex: Knex): Promise<void> {
|
||||
if (!(await knex.schema.hasTable(TableName.Secret))) {
|
||||
await knex.schema.createTable(TableName.Secret, (t) => {
|
||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||
t.integer("version").defaultTo(1);
|
||||
t.integer("version").defaultTo(1).notNullable();
|
||||
t.string("type").notNullable().defaultTo(SecretType.Shared);
|
||||
// t.text("secretKeyHash").notNullable();
|
||||
// t.text("secretValueHash");
|
||||
|
||||
@@ -7,7 +7,7 @@ export async function up(knex: Knex): Promise<void> {
|
||||
if (!(await knex.schema.hasTable(TableName.SecretVersion))) {
|
||||
await knex.schema.createTable(TableName.SecretVersion, (t) => {
|
||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||
t.integer("version").defaultTo(1);
|
||||
t.integer("version").defaultTo(1).notNullable();
|
||||
t.string("type").notNullable().defaultTo(SecretType.Shared);
|
||||
t.text("secretBlindIndex").notNullable();
|
||||
t.text("secretKeyCiphertext").notNullable();
|
||||
|
||||
33
backend-pg/src/db/migrations/20240107153439_saml-config.ts
Normal file
33
backend-pg/src/db/migrations/20240107153439_saml-config.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
if (!(await knex.schema.hasTable(TableName.SamlConfig))) {
|
||||
await knex.schema.createTable(TableName.SamlConfig, (t) => {
|
||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||
t.string("authProvider").notNullable();
|
||||
t.boolean("isActive").notNullable();
|
||||
t.string("encryptedEntryPoint");
|
||||
t.string("entryPointIV");
|
||||
t.string("entryPointTag");
|
||||
t.string("encryptedIssuer");
|
||||
t.string("issuerTag");
|
||||
t.string("issuerIV");
|
||||
t.string("encryptedCert");
|
||||
t.string("certIV");
|
||||
t.string("certTag");
|
||||
t.timestamps(true, true, true);
|
||||
t.uuid("orgId").notNullable().unique();
|
||||
t.foreign("orgId").references("id").inTable(TableName.Organization);
|
||||
});
|
||||
}
|
||||
|
||||
await createOnUpdateTrigger(knex, TableName.SamlConfig);
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
await knex.schema.dropTableIfExists(TableName.SamlConfig);
|
||||
await dropOnUpdateTrigger(knex, TableName.SamlConfig);
|
||||
}
|
||||
35
backend-pg/src/db/migrations/20240107163155_org-bot.ts
Normal file
35
backend-pg/src/db/migrations/20240107163155_org-bot.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
if (!(await knex.schema.hasTable(TableName.OrgBot))) {
|
||||
await knex.schema.createTable(TableName.OrgBot, (t) => {
|
||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||
t.string("name").notNullable();
|
||||
t.text("publicKey").notNullable();
|
||||
t.text("encryptedSymmetricKey").notNullable();
|
||||
t.text("symmetricKeyIV").notNullable();
|
||||
t.text("symmetricKeyTag").notNullable();
|
||||
t.string("symmetricKeyAlgorithm").notNullable();
|
||||
t.string("symmetricKeyKeyEncoding").notNullable();
|
||||
t.text("encryptedPrivateKey").notNullable();
|
||||
t.text("privateKeyIV").notNullable();
|
||||
t.text("privateKeyTag").notNullable();
|
||||
t.string("privateKeyAlgorithm").notNullable();
|
||||
t.string("privateKeyKeyEncoding").notNullable();
|
||||
// one to one relationship
|
||||
t.uuid("orgId").notNullable().unique();
|
||||
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
|
||||
t.timestamps(true, true, true);
|
||||
});
|
||||
}
|
||||
|
||||
await createOnUpdateTrigger(knex, TableName.OrgBot);
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
await knex.schema.dropTableIfExists(TableName.OrgBot);
|
||||
await dropOnUpdateTrigger(knex, TableName.OrgBot);
|
||||
}
|
||||
29
backend-pg/src/db/migrations/20240108134148_audit-log.ts
Normal file
29
backend-pg/src/db/migrations/20240108134148_audit-log.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
if (!(await knex.schema.hasTable(TableName.AuditLog))) {
|
||||
await knex.schema.createTable(TableName.AuditLog, (t) => {
|
||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||
t.string("actor").notNullable();
|
||||
t.jsonb("actorMetadata").notNullable();
|
||||
t.string("ipAddress");
|
||||
t.string("eventType").notNullable();
|
||||
t.jsonb("eventMetadata");
|
||||
t.string("userAgent");
|
||||
t.string("userAgentType");
|
||||
t.datetime("expiresAt");
|
||||
t.timestamps(true, true, true);
|
||||
// no trigger needed as this collection is append only
|
||||
t.uuid("orgId");
|
||||
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
|
||||
t.uuid("projectId");
|
||||
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
await knex.schema.dropTableIfExists(TableName.AuditLog);
|
||||
}
|
||||
28
backend-pg/src/db/schemas/audit-logs.ts
Normal file
28
backend-pg/src/db/schemas/audit-logs.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
// Code generated by automation script, DO NOT EDIT.
|
||||
// Automated by pulling database and generating zod schema
|
||||
// To update. Just run npm run generate:schema
|
||||
// Written by akhilmhdh.
|
||||
|
||||
import { z } from "zod";
|
||||
|
||||
import { TImmutableDBKeys } from "./models";
|
||||
|
||||
export const AuditLogsSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
actor: z.string(),
|
||||
actorMetadata: z.unknown(),
|
||||
ipAddress: z.string().nullable().optional(),
|
||||
eventType: z.string(),
|
||||
eventMetadata: z.unknown().nullable().optional(),
|
||||
userAgent: z.string().nullable().optional(),
|
||||
userAgentType: z.string().nullable().optional(),
|
||||
expiresAt: z.date().nullable().optional(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
orgId: z.string().uuid().nullable().optional(),
|
||||
projectId: z.string().uuid().nullable().optional(),
|
||||
});
|
||||
|
||||
export type TAuditLogs = z.infer<typeof AuditLogsSchema>;
|
||||
export type TAuditLogsInsert = Omit<TAuditLogs, TImmutableDBKeys>;
|
||||
export type TAuditLogsUpdate = Partial<Omit<TAuditLogs, TImmutableDBKeys>>;
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from "./api-keys";
|
||||
export * from "./audit-logs";
|
||||
export * from "./auth-token-sessions";
|
||||
export * from "./auth-tokens";
|
||||
export * from "./backup-private-key";
|
||||
@@ -12,6 +13,7 @@ export * from "./incident-contacts";
|
||||
export * from "./integration-auths";
|
||||
export * from "./integrations";
|
||||
export * from "./models";
|
||||
export * from "./org-bots";
|
||||
export * from "./org-memberships";
|
||||
export * from "./org-roles";
|
||||
export * from "./organizations";
|
||||
@@ -23,6 +25,7 @@ export * from "./project-roles";
|
||||
export * from "./projects";
|
||||
export * from "./sa-request-secret-tags";
|
||||
export * from "./sa-request-secrets";
|
||||
export * from "./saml-configs";
|
||||
export * from "./sap-approvers";
|
||||
export * from "./sar-reviewers";
|
||||
export * from "./secret-approval-policies";
|
||||
|
||||
@@ -9,6 +9,7 @@ export enum TableName {
|
||||
Organization = "organizations",
|
||||
OrgMembership = "org_memberships",
|
||||
OrgRoles = "org_roles",
|
||||
OrgBot = "org_bots",
|
||||
IncidentContact = "incident_contacts",
|
||||
UserAction = "user_actions",
|
||||
SuperAdmin = "super_admin",
|
||||
@@ -47,6 +48,8 @@ export enum TableName {
|
||||
SarSecretTag = "sa_request_secret_tags",
|
||||
SecretRotation = "secret_rotations",
|
||||
SecretRotationOutput = "secret_rotation_outputs",
|
||||
SamlConfig = "saml_configs",
|
||||
AuditLog = "audit_logs",
|
||||
// junction tables
|
||||
JnSecretTag = "secret_tag_junction",
|
||||
JnSecretVersionTag = "secret_version_tag_junction"
|
||||
|
||||
31
backend-pg/src/db/schemas/org-bots.ts
Normal file
31
backend-pg/src/db/schemas/org-bots.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
// Code generated by automation script, DO NOT EDIT.
|
||||
// Automated by pulling database and generating zod schema
|
||||
// To update. Just run npm run generate:schema
|
||||
// Written by akhilmhdh.
|
||||
|
||||
import { z } from "zod";
|
||||
|
||||
import { TImmutableDBKeys } from "./models";
|
||||
|
||||
export const OrgBotsSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
name: z.string(),
|
||||
publicKey: z.string(),
|
||||
encryptedSymmetricKey: z.string(),
|
||||
symmetricKeyIV: z.string(),
|
||||
symmetricKeyTag: z.string(),
|
||||
symmetricKeyAlgorithm: z.string(),
|
||||
symmetricKeyKeyEncoding: z.string(),
|
||||
encryptedPrivateKey: z.string(),
|
||||
privateKeyIV: z.string(),
|
||||
privateKeyTag: z.string(),
|
||||
privateKeyAlgorithm: z.string(),
|
||||
privateKeyKeyEncoding: z.string(),
|
||||
orgId: z.string().uuid(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
});
|
||||
|
||||
export type TOrgBots = z.infer<typeof OrgBotsSchema>;
|
||||
export type TOrgBotsInsert = Omit<TOrgBots, TImmutableDBKeys>;
|
||||
export type TOrgBotsUpdate = Partial<Omit<TOrgBots, TImmutableDBKeys>>;
|
||||
30
backend-pg/src/db/schemas/saml-configs.ts
Normal file
30
backend-pg/src/db/schemas/saml-configs.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
// Code generated by automation script, DO NOT EDIT.
|
||||
// Automated by pulling database and generating zod schema
|
||||
// To update. Just run npm run generate:schema
|
||||
// Written by akhilmhdh.
|
||||
|
||||
import { z } from "zod";
|
||||
|
||||
import { TImmutableDBKeys } from "./models";
|
||||
|
||||
export const SamlConfigsSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
authProvider: z.string(),
|
||||
isActive: z.boolean(),
|
||||
encryptedEntryPoint: z.string().nullable().optional(),
|
||||
entryPointIV: z.string().nullable().optional(),
|
||||
entryPointTag: z.string().nullable().optional(),
|
||||
encryptedIssuer: z.string().nullable().optional(),
|
||||
issuerTag: z.string().nullable().optional(),
|
||||
issuerIV: z.string().nullable().optional(),
|
||||
encryptedCert: z.string().nullable().optional(),
|
||||
certIV: z.string().nullable().optional(),
|
||||
certTag: z.string().nullable().optional(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
orgId: z.string().uuid(),
|
||||
});
|
||||
|
||||
export type TSamlConfigs = z.infer<typeof SamlConfigsSchema>;
|
||||
export type TSamlConfigsInsert = Omit<TSamlConfigs, TImmutableDBKeys>;
|
||||
export type TSamlConfigsUpdate = Partial<Omit<TSamlConfigs, TImmutableDBKeys>>;
|
||||
@@ -9,7 +9,7 @@ import { TImmutableDBKeys } from "./models";
|
||||
|
||||
export const SecretVersionsSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
version: z.number().default(1).nullable().optional(),
|
||||
version: z.number().default(1),
|
||||
type: z.string().default("shared"),
|
||||
secretBlindIndex: z.string(),
|
||||
secretKeyCiphertext: z.string(),
|
||||
|
||||
@@ -9,7 +9,7 @@ import { TImmutableDBKeys } from "./models";
|
||||
|
||||
export const SecretsSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
version: z.number().default(1).nullable().optional(),
|
||||
version: z.number().default(1),
|
||||
type: z.string().default("shared"),
|
||||
secretBlindIndex: z.string(),
|
||||
secretKeyCiphertext: z.string(),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { registerOrgRoleRouter } from "./org-role-router";
|
||||
import { registerProjectRoleRouter } from "./project-role-router";
|
||||
import { registerProjectRouter } from "./project-router";
|
||||
import { registerSamlRouter } from "./saml-router";
|
||||
import { registerSecretApprovalPolicyRouter } from "./secret-approval-policy-router";
|
||||
import { registerSecretApprovalRequestRouter } from "./secret-approval-request-router";
|
||||
import { registerSecretRotationProviderRouter } from "./secret-rotation-provider-router";
|
||||
@@ -25,5 +26,6 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => {
|
||||
await server.register(registerSecretRotationProviderRouter, {
|
||||
prefix: "/secret-rotation-providers"
|
||||
});
|
||||
await server.register(registerSamlRouter, { prefix: "/sso" });
|
||||
await server.register(registerSecretRotationRouter, { prefix: "/secret-rotations" });
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { SecretSnapshotsSchema } from "@app/db/schemas";
|
||||
import { AuditLogsSchema, SecretSnapshotsSchema } from "@app/db/schemas";
|
||||
import { EventType, UserAgentType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@@ -65,4 +66,74 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
return { count };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:workspaceId/audit-logs",
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
}),
|
||||
querystring: z.object({
|
||||
eventType: z.nativeEnum(EventType).optional(),
|
||||
userAgentType: z.nativeEnum(UserAgentType).optional(),
|
||||
startDate: z.string().datetime().optional(),
|
||||
endDate: z.string().datetime().optional(),
|
||||
offset: z.coerce.number().default(0),
|
||||
limit: z.coerce.number().default(20),
|
||||
actor: z.string().optional()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
auditLogs: AuditLogsSchema.omit({
|
||||
eventMetadata: true,
|
||||
eventType: true,
|
||||
actor: true,
|
||||
actorMetadata: true
|
||||
})
|
||||
.merge(
|
||||
z.object({
|
||||
event: z.object({
|
||||
type: z.string(),
|
||||
metadata: z.any()
|
||||
}),
|
||||
actor: z.object({
|
||||
type: z.string(),
|
||||
metadata: z.any()
|
||||
})
|
||||
})
|
||||
)
|
||||
.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const auditLogs = await server.services.auditLog.listProjectAuditLogs({
|
||||
actorId: req.permission.id,
|
||||
projectId: req.params.workspaceId,
|
||||
...req.query,
|
||||
auditLogActor: req.query.actor,
|
||||
actor: req.permission.type
|
||||
});
|
||||
return { auditLogs };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:workspaceId/audit-logs/filters/actors",
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
actors: z.string().array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async () => ({ actors: [] })
|
||||
});
|
||||
};
|
||||
|
||||
237
backend-pg/src/ee/routes/v1/saml-router.ts
Normal file
237
backend-pg/src/ee/routes/v1/saml-router.ts
Normal file
@@ -0,0 +1,237 @@
|
||||
import { Authenticator } from "@fastify/passport";
|
||||
import fastifySession from "@fastify/session";
|
||||
import { MultiSamlStrategy } from "@node-saml/passport-saml";
|
||||
import { z } from "zod";
|
||||
|
||||
import { SamlConfigsSchema } from "@app/db/schemas";
|
||||
import { SamlProviders } from "@app/ee/services/saml-config/saml-config-types";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
type TSAMLConfig = {
|
||||
callbackUrl: string;
|
||||
entryPoint: string;
|
||||
issuer: string;
|
||||
cert: string;
|
||||
audience: string;
|
||||
wantAuthnResponseSigned?: boolean;
|
||||
};
|
||||
|
||||
export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
||||
const appCfg = getConfig();
|
||||
const passport = new Authenticator({ key: "saml", userProperty: "passportUser" });
|
||||
await server.register(fastifySession, { secret: appCfg.COOKIE_SECRET_SIGN_KEY });
|
||||
await server.register(passport.initialize());
|
||||
await server.register(passport.secureSession());
|
||||
server.decorateRequest("ssoConfig", null);
|
||||
passport.use(
|
||||
new MultiSamlStrategy(
|
||||
{
|
||||
passReqToCallback: true,
|
||||
getSamlOptions: async (req, done) => {
|
||||
const { ssoIdentifier } = req.params;
|
||||
if (!ssoIdentifier) throw new BadRequestError({ message: "Missing sso identitier" });
|
||||
|
||||
const ssoConfig = await server.services.saml.getSaml({
|
||||
type: "ssoId",
|
||||
id: ssoIdentifier
|
||||
});
|
||||
if (!ssoConfig) throw new BadRequestError({ message: "SSO config not found" });
|
||||
|
||||
const samlConfig: TSAMLConfig = {
|
||||
callbackUrl: `${appCfg.SITE_URL}/api/v1/sso/saml2/${ssoIdentifier}`,
|
||||
entryPoint: ssoConfig.entryPoint,
|
||||
issuer: ssoConfig.issuer,
|
||||
cert: ssoConfig.cert,
|
||||
audience: appCfg.SITE_URL || ""
|
||||
};
|
||||
if (ssoConfig.authProvider === SamlProviders.JUMPCLOUD_SAML) {
|
||||
samlConfig.wantAuthnResponseSigned = false;
|
||||
}
|
||||
if (ssoConfig.authProvider === SamlProviders.AZURE_SAML) {
|
||||
if (req.body.RelayState && JSON.parse(req.body.RelayState).spIntiaited) {
|
||||
samlConfig.audience = `spn:${ssoConfig.issuer}`;
|
||||
}
|
||||
}
|
||||
req.ssoConfig = ssoConfig;
|
||||
done(null, samlConfig);
|
||||
}
|
||||
},
|
||||
async (req, profile, cb) => {
|
||||
console.log(req.ssoConfig);
|
||||
console.log(profile);
|
||||
try {
|
||||
const serverCfg = server.services.superAdmin.getServerCfg();
|
||||
if (!profile) throw new BadRequestError({ message: "Missing profile" });
|
||||
const { email, firstName } = profile;
|
||||
if (!email || !firstName)
|
||||
throw new BadRequestError({ message: "Invalid request. Missing email or first name" });
|
||||
|
||||
const { isUserCompleted, providerAuthToken } = await server.services.saml.samlLogin({
|
||||
email,
|
||||
firstName: profile.firstName as string,
|
||||
lastName: profile.lastName as string,
|
||||
isSignupAllowed: Boolean(serverCfg.allowSignUp),
|
||||
relayState: (req.body as { RelayState?: string }).RelayState,
|
||||
authProvider: req.ssoConfig.authProvider,
|
||||
orgId: req.ssoConfig.orgId
|
||||
});
|
||||
cb(null, { isUserCompleted, providerAuthToken });
|
||||
} catch (error) {
|
||||
cb(null, {});
|
||||
}
|
||||
},
|
||||
() => {}
|
||||
)
|
||||
);
|
||||
|
||||
server.route({
|
||||
url: "/redirect/saml2/:ssoIdentifier",
|
||||
method: "GET",
|
||||
schema: {
|
||||
params: z.object({
|
||||
ssoIdentifier: z.string().trim()
|
||||
}),
|
||||
querystring: z.object({
|
||||
callback_port: z.string().optional()
|
||||
})
|
||||
},
|
||||
preValidation: (req, res) =>
|
||||
(
|
||||
passport.authenticate("saml", {
|
||||
failureRedirect: "/",
|
||||
additionalParams: {
|
||||
RelayState: JSON.stringify({
|
||||
spInitiated: true,
|
||||
callbackPort: req.query.callback_port ?? ""
|
||||
})
|
||||
}
|
||||
} as any) as any
|
||||
)(req, res),
|
||||
handler: () => {}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/saml2/:ssoIdentifier",
|
||||
method: "GET",
|
||||
schema: {
|
||||
params: z.object({
|
||||
ssoIdentifier: z.string().trim()
|
||||
})
|
||||
},
|
||||
preValidation: passport.authenticate("saml", {
|
||||
session: false,
|
||||
failureFlash: true,
|
||||
failureRedirect: "/login/provider/error"
|
||||
// this is due to zod type difference
|
||||
}) as any,
|
||||
handler: (req, res) => {
|
||||
if (req.passportUser.isCompleted) {
|
||||
return res.redirect(
|
||||
`${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(
|
||||
req.passportUser.providerAuthToken
|
||||
)}`
|
||||
);
|
||||
}
|
||||
return res.redirect(
|
||||
`${appCfg.SITE_URL}/signup/sso?token=${encodeURIComponent(
|
||||
req.passportUser.providerAuthToken
|
||||
)}`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/config",
|
||||
method: "GET",
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
querystring: z.object({
|
||||
organizationId: z.string().trim()
|
||||
}),
|
||||
response: {
|
||||
200: z
|
||||
.object({
|
||||
id: z.string(),
|
||||
organization: z.string(),
|
||||
orgId: z.string(),
|
||||
authProvider: z.string(),
|
||||
isActive: z.boolean(),
|
||||
entryPoint: z.string(),
|
||||
issuer: z.string(),
|
||||
cert: z.string()
|
||||
})
|
||||
.optional()
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const saml = await server.services.saml.getSaml({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
orgId: req.query.organizationId,
|
||||
type: "org"
|
||||
});
|
||||
return saml;
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/config",
|
||||
method: "POST",
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
body: z.object({
|
||||
organizationId: z.string(),
|
||||
authProvider: z.nativeEnum(SamlProviders),
|
||||
isActive: z.boolean(),
|
||||
entryPoint: z.string(),
|
||||
issuer: z.string(),
|
||||
cert: z.string()
|
||||
}),
|
||||
response: {
|
||||
200: SamlConfigsSchema
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const saml = await server.services.saml.createSamlCfg({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
orgId: req.body.organizationId,
|
||||
...req.body
|
||||
});
|
||||
return saml;
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/config",
|
||||
method: "PATCH",
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
body: z
|
||||
.object({
|
||||
authProvider: z.nativeEnum(SamlProviders),
|
||||
isActive: z.boolean(),
|
||||
entryPoint: z.string(),
|
||||
issuer: z.string(),
|
||||
cert: z.string()
|
||||
})
|
||||
.partial()
|
||||
.merge(z.object({ organizationId: z.string() })),
|
||||
response: {
|
||||
200: SamlConfigsSchema
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const saml = await server.services.saml.updateSamlCfg({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
orgId: req.body.organizationId,
|
||||
...req.body
|
||||
});
|
||||
return saml;
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
SecretsSchema,
|
||||
SecretVersionsSchema
|
||||
} from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import {
|
||||
ApprovalStatus,
|
||||
RequestState
|
||||
@@ -230,6 +231,23 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
||||
approvalId: req.params.id,
|
||||
status: req.body.status
|
||||
});
|
||||
|
||||
const isClosing = approval.status === RequestState.Closed;
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId: approval.projectId,
|
||||
...req.auditLogInfo,
|
||||
event: {
|
||||
type: isClosing ? EventType.SECRET_APPROVAL_CLOSED : EventType.SECRET_APPROVAL_REOPENED,
|
||||
metadata: {
|
||||
[isClosing ? ("closedBy" as const) : ("reopenedBy" as const)]:
|
||||
approval.statusChangeBy as string,
|
||||
secretApprovalRequestId: approval.id,
|
||||
secretApprovalRequestSlug: approval.slug
|
||||
} as any
|
||||
// akhilmhdh: had to apply any to avoid ts issue with this
|
||||
}
|
||||
});
|
||||
|
||||
return { approval };
|
||||
}
|
||||
});
|
||||
|
||||
61
backend-pg/src/ee/services/audit-log/audit-log-dal.ts
Normal file
61
backend-pg/src/ee/services/audit-log/audit-log-dal.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TDbClient } from "@app/db";
|
||||
import { TableName } from "@app/db/schemas";
|
||||
import { ormify, stripUndefinedInWhere } from "@app/lib/knex";
|
||||
|
||||
export type TAuditLogDalFactory = ReturnType<typeof auditLogDalFactory>;
|
||||
|
||||
type TFindQuery = {
|
||||
actor?: string;
|
||||
projectId?: string;
|
||||
orgId?: string;
|
||||
eventType?: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
userAgentType?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
};
|
||||
|
||||
export const auditLogDalFactory = (db: TDbClient) => {
|
||||
const auditLogOrm = ormify(db, TableName.AuditLog);
|
||||
|
||||
const find = async (
|
||||
{
|
||||
orgId,
|
||||
projectId,
|
||||
userAgentType,
|
||||
startDate,
|
||||
endDate,
|
||||
limit = 20,
|
||||
offset = 0,
|
||||
actor,
|
||||
eventType
|
||||
}: TFindQuery,
|
||||
tx?: Knex
|
||||
) => {
|
||||
const sqlQuery = (tx || db)(TableName.AuditLog)
|
||||
.where(
|
||||
stripUndefinedInWhere({
|
||||
projectId,
|
||||
orgId,
|
||||
eventType,
|
||||
actor,
|
||||
userAgentType
|
||||
})
|
||||
)
|
||||
.limit(limit)
|
||||
.offset(offset);
|
||||
if (startDate) {
|
||||
sqlQuery.where("createdAt", ">=", startDate);
|
||||
}
|
||||
if (endDate) {
|
||||
sqlQuery.where("createdAt", "<=", endDate);
|
||||
}
|
||||
const docs = await sqlQuery;
|
||||
return docs;
|
||||
};
|
||||
|
||||
return { ...auditLogOrm, find };
|
||||
};
|
||||
46
backend-pg/src/ee/services/audit-log/audit-log-queue.ts
Normal file
46
backend-pg/src/ee/services/audit-log/audit-log-queue.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
|
||||
|
||||
import { TAuditLogDalFactory } from "./audit-log-dal";
|
||||
import { TCreateAuditLogDTO } from "./audit-log-types";
|
||||
|
||||
type TAuditLogQueueServiceFactoryDep = {
|
||||
auditLogDal: TAuditLogDalFactory;
|
||||
queueService: TQueueServiceFactory;
|
||||
};
|
||||
|
||||
export type TAuditLogQueueServiceFactory = ReturnType<typeof auditLogQueueServiceFactory>;
|
||||
|
||||
export const auditLogQueueServiceFactory = ({
|
||||
auditLogDal,
|
||||
queueService
|
||||
}: TAuditLogQueueServiceFactoryDep) => {
|
||||
const pushToLog = async (data: TCreateAuditLogDTO) => {
|
||||
await queueService.queue(QueueName.AuditLog, QueueJobs.AuditLog, data, {
|
||||
removeOnFail: {
|
||||
count: 5
|
||||
},
|
||||
removeOnComplete: true
|
||||
});
|
||||
};
|
||||
|
||||
queueService.start(QueueName.AuditLog, async (job) => {
|
||||
const { actor, orgId, event, ipAddress, projectId, userAgent, userAgentType } = job.data;
|
||||
const MS_IN_DAY = 24 * 60 * 60 * 1000;
|
||||
await auditLogDal.create({
|
||||
actor: actor.type,
|
||||
actorMetadata: actor.metadata,
|
||||
userAgent,
|
||||
projectId,
|
||||
ipAddress,
|
||||
orgId,
|
||||
eventType: event.type,
|
||||
expiresAt: new Date(Date.now() + 30 * MS_IN_DAY),
|
||||
eventMetadata: event.metadata,
|
||||
userAgentType
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
pushToLog
|
||||
};
|
||||
};
|
||||
73
backend-pg/src/ee/services/audit-log/audit-log-service.ts
Normal file
73
backend-pg/src/ee/services/audit-log/audit-log-service.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
|
||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission";
|
||||
import { TAuditLogDalFactory } from "./audit-log-dal";
|
||||
import { TAuditLogQueueServiceFactory } from "./audit-log-queue";
|
||||
import { EventType, TCreateAuditLogDTO, TListProjectAuditLogDTO } from "./audit-log-types";
|
||||
|
||||
type TAuditLogServiceFactoryDep = {
|
||||
auditLogDal: TAuditLogDalFactory;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
||||
auditLogQueue: TAuditLogQueueServiceFactory;
|
||||
};
|
||||
|
||||
export type TAuditLogServiceFactory = ReturnType<typeof auditLogServiceFactory>;
|
||||
|
||||
export const auditLogServiceFactory = ({
|
||||
auditLogDal,
|
||||
auditLogQueue,
|
||||
permissionService
|
||||
}: TAuditLogServiceFactoryDep) => {
|
||||
const listProjectAuditLogs = async ({
|
||||
userAgentType,
|
||||
eventType,
|
||||
offset,
|
||||
limit,
|
||||
endDate,
|
||||
startDate,
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
auditLogActor
|
||||
}: TListProjectAuditLogDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.AuditLogs
|
||||
);
|
||||
const auditLogs = await auditLogDal.find({
|
||||
startDate,
|
||||
endDate,
|
||||
limit,
|
||||
offset,
|
||||
eventType,
|
||||
userAgentType,
|
||||
actor: auditLogActor,
|
||||
projectId
|
||||
});
|
||||
return auditLogs.map(
|
||||
({ eventType: logEventType, actor: eActor, actorMetadata, eventMetadata, ...el }) => ({
|
||||
...el,
|
||||
event: { type: logEventType, metadata: eventMetadata },
|
||||
actor: { type: eActor, metadata: actorMetadata }
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const createAuditLog = async (data: TCreateAuditLogDTO) => {
|
||||
// add all cases in which project id or org id cannot be added
|
||||
if (data.event.type !== EventType.LOGIN_IDENTITY_UNIVERSAL_AUTH) {
|
||||
if (!data.projectId && !data.orgId)
|
||||
throw new BadRequestError({ message: "Must either project id or org id" });
|
||||
}
|
||||
return auditLogQueue.pushToLog(data);
|
||||
};
|
||||
|
||||
return {
|
||||
createAuditLog,
|
||||
listProjectAuditLogs
|
||||
};
|
||||
};
|
||||
674
backend-pg/src/ee/services/audit-log/audit-log-types.ts
Normal file
674
backend-pg/src/ee/services/audit-log/audit-log-types.ts
Normal file
@@ -0,0 +1,674 @@
|
||||
import { TProjectPermission } from "@app/lib/types";
|
||||
import { ActorType } from "@app/services/auth/auth-type";
|
||||
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
|
||||
|
||||
export type TListProjectAuditLogDTO = {
|
||||
auditLogActor?: string;
|
||||
projectId: string;
|
||||
eventType?: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
userAgentType?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
} & TProjectPermission;
|
||||
|
||||
export type TCreateAuditLogDTO = {
|
||||
event: Event;
|
||||
actor: UserActor | IdentityActor | ServiceActor;
|
||||
orgId?: string;
|
||||
projectId?: string;
|
||||
} & BaseAuthData;
|
||||
|
||||
interface BaseAuthData {
|
||||
ipAddress?: string;
|
||||
userAgent?: string;
|
||||
userAgentType?: UserAgentType;
|
||||
}
|
||||
|
||||
export enum UserAgentType {
|
||||
WEB = "web",
|
||||
CLI = "cli",
|
||||
K8_OPERATOR = "k8-operator",
|
||||
TERRAFORM = "terraform",
|
||||
OTHER = "other",
|
||||
PYTHON_SDK = "InfisicalPythonSDK",
|
||||
NODE_SDK = "InfisicalNodeSDK"
|
||||
}
|
||||
|
||||
export enum EventType {
|
||||
GET_SECRETS = "get-secrets",
|
||||
GET_SECRET = "get-secret",
|
||||
REVEAL_SECRET = "reveal-secret",
|
||||
CREATE_SECRET = "create-secret",
|
||||
CREATE_SECRETS = "create-secrets",
|
||||
UPDATE_SECRET = "update-secret",
|
||||
UPDATE_SECRETS = "update-secrets",
|
||||
DELETE_SECRET = "delete-secret",
|
||||
DELETE_SECRETS = "delete-secrets",
|
||||
GET_WORKSPACE_KEY = "get-workspace-key",
|
||||
AUTHORIZE_INTEGRATION = "authorize-integration",
|
||||
UNAUTHORIZE_INTEGRATION = "unauthorize-integration",
|
||||
CREATE_INTEGRATION = "create-integration",
|
||||
DELETE_INTEGRATION = "delete-integration",
|
||||
ADD_TRUSTED_IP = "add-trusted-ip",
|
||||
UPDATE_TRUSTED_IP = "update-trusted-ip",
|
||||
DELETE_TRUSTED_IP = "delete-trusted-ip",
|
||||
CREATE_SERVICE_TOKEN = "create-service-token", // v2
|
||||
DELETE_SERVICE_TOKEN = "delete-service-token", // v2
|
||||
CREATE_IDENTITY = "create-identity",
|
||||
UPDATE_IDENTITY = "update-identity",
|
||||
DELETE_IDENTITY = "delete-identity",
|
||||
LOGIN_IDENTITY_UNIVERSAL_AUTH = "login-identity-universal-auth",
|
||||
ADD_IDENTITY_UNIVERSAL_AUTH = "add-identity-universal-auth",
|
||||
UPDATE_IDENTITY_UNIVERSAL_AUTH = "update-identity-universal-auth",
|
||||
GET_IDENTITY_UNIVERSAL_AUTH = "get-identity-universal-auth",
|
||||
CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "create-identity-universal-auth-client-secret",
|
||||
REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "revoke-identity-universal-auth-client-secret",
|
||||
GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRETS = "get-identity-universal-auth-client-secret",
|
||||
CREATE_ENVIRONMENT = "create-environment",
|
||||
UPDATE_ENVIRONMENT = "update-environment",
|
||||
DELETE_ENVIRONMENT = "delete-environment",
|
||||
ADD_WORKSPACE_MEMBER = "add-workspace-member",
|
||||
ADD_BATCH_WORKSPACE_MEMBER = "add-workspace-members",
|
||||
REMOVE_WORKSPACE_MEMBER = "remove-workspace-member",
|
||||
CREATE_FOLDER = "create-folder",
|
||||
UPDATE_FOLDER = "update-folder",
|
||||
DELETE_FOLDER = "delete-folder",
|
||||
CREATE_WEBHOOK = "create-webhook",
|
||||
UPDATE_WEBHOOK_STATUS = "update-webhook-status",
|
||||
DELETE_WEBHOOK = "delete-webhook",
|
||||
GET_SECRET_IMPORTS = "get-secret-imports",
|
||||
CREATE_SECRET_IMPORT = "create-secret-import",
|
||||
UPDATE_SECRET_IMPORT = "update-secret-import",
|
||||
DELETE_SECRET_IMPORT = "delete-secret-import",
|
||||
UPDATE_USER_WORKSPACE_ROLE = "update-user-workspace-role",
|
||||
UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS = "update-user-workspace-denied-permissions",
|
||||
SECRET_APPROVAL_MERGED = "secret-approval-merged",
|
||||
SECRET_APPROVAL_REQUEST = "secret-approval-request",
|
||||
SECRET_APPROVAL_CLOSED = "secret-approval-closed",
|
||||
SECRET_APPROVAL_REOPENED = "secret-approval-reopened"
|
||||
}
|
||||
|
||||
interface UserActorMetadata {
|
||||
userId: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
interface ServiceActorMetadata {
|
||||
serviceId: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface IdentityActorMetadata {
|
||||
identityId: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface UserActor {
|
||||
type: ActorType.USER;
|
||||
metadata: UserActorMetadata;
|
||||
}
|
||||
|
||||
export interface ServiceActor {
|
||||
type: ActorType.SERVICE;
|
||||
metadata: ServiceActorMetadata;
|
||||
}
|
||||
|
||||
export interface IdentityActor {
|
||||
type: ActorType.IDENTITY;
|
||||
metadata: IdentityActorMetadata;
|
||||
}
|
||||
|
||||
export type Actor = UserActor | ServiceActor | IdentityActor;
|
||||
|
||||
interface GetSecretsEvent {
|
||||
type: EventType.GET_SECRETS;
|
||||
metadata: {
|
||||
environment: string;
|
||||
secretPath: string;
|
||||
numberOfSecrets: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface GetSecretEvent {
|
||||
type: EventType.GET_SECRET;
|
||||
metadata: {
|
||||
environment: string;
|
||||
secretPath: string;
|
||||
secretId: string;
|
||||
secretKey: string;
|
||||
secretVersion: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface CreateSecretEvent {
|
||||
type: EventType.CREATE_SECRET;
|
||||
metadata: {
|
||||
environment: string;
|
||||
secretPath: string;
|
||||
secretId: string;
|
||||
secretKey: string;
|
||||
secretVersion: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface CreateSecretBatchEvent {
|
||||
type: EventType.CREATE_SECRETS;
|
||||
metadata: {
|
||||
environment: string;
|
||||
secretPath: string;
|
||||
secrets: Array<{ secretId: string; secretKey: string; secretVersion: number }>;
|
||||
};
|
||||
}
|
||||
|
||||
interface UpdateSecretEvent {
|
||||
type: EventType.UPDATE_SECRET;
|
||||
metadata: {
|
||||
environment: string;
|
||||
secretPath: string;
|
||||
secretId: string;
|
||||
secretKey: string;
|
||||
secretVersion: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface UpdateSecretBatchEvent {
|
||||
type: EventType.UPDATE_SECRETS;
|
||||
metadata: {
|
||||
environment: string;
|
||||
secretPath: string;
|
||||
secrets: Array<{ secretId: string; secretKey: string; secretVersion: number }>;
|
||||
};
|
||||
}
|
||||
|
||||
interface DeleteSecretEvent {
|
||||
type: EventType.DELETE_SECRET;
|
||||
metadata: {
|
||||
environment: string;
|
||||
secretPath: string;
|
||||
secretId: string;
|
||||
secretKey: string;
|
||||
secretVersion: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface DeleteSecretBatchEvent {
|
||||
type: EventType.DELETE_SECRETS;
|
||||
metadata: {
|
||||
environment: string;
|
||||
secretPath: string;
|
||||
secrets: Array<{ secretId: string; secretKey: string; secretVersion: number }>;
|
||||
};
|
||||
}
|
||||
|
||||
interface GetWorkspaceKeyEvent {
|
||||
type: EventType.GET_WORKSPACE_KEY;
|
||||
metadata: {
|
||||
keyId: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface AuthorizeIntegrationEvent {
|
||||
type: EventType.AUTHORIZE_INTEGRATION;
|
||||
metadata: {
|
||||
integration: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface UnauthorizeIntegrationEvent {
|
||||
type: EventType.UNAUTHORIZE_INTEGRATION;
|
||||
metadata: {
|
||||
integration: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface CreateIntegrationEvent {
|
||||
type: EventType.CREATE_INTEGRATION;
|
||||
metadata: {
|
||||
integrationId: string;
|
||||
integration: string; // TODO: fix type
|
||||
environment: string;
|
||||
secretPath: string;
|
||||
url?: string;
|
||||
app?: string;
|
||||
appId?: string;
|
||||
targetEnvironment?: string;
|
||||
targetEnvironmentId?: string;
|
||||
targetService?: string;
|
||||
targetServiceId?: string;
|
||||
path?: string;
|
||||
region?: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface DeleteIntegrationEvent {
|
||||
type: EventType.DELETE_INTEGRATION;
|
||||
metadata: {
|
||||
integrationId: string;
|
||||
integration: string; // TODO: fix type
|
||||
environment: string;
|
||||
secretPath: string;
|
||||
url?: string;
|
||||
app?: string;
|
||||
appId?: string;
|
||||
targetEnvironment?: string;
|
||||
targetEnvironmentId?: string;
|
||||
targetService?: string;
|
||||
targetServiceId?: string;
|
||||
path?: string;
|
||||
region?: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface AddTrustedIPEvent {
|
||||
type: EventType.ADD_TRUSTED_IP;
|
||||
metadata: {
|
||||
trustedIpId: string;
|
||||
ipAddress: string;
|
||||
prefix?: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface UpdateTrustedIPEvent {
|
||||
type: EventType.UPDATE_TRUSTED_IP;
|
||||
metadata: {
|
||||
trustedIpId: string;
|
||||
ipAddress: string;
|
||||
prefix?: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface DeleteTrustedIPEvent {
|
||||
type: EventType.DELETE_TRUSTED_IP;
|
||||
metadata: {
|
||||
trustedIpId: string;
|
||||
ipAddress: string;
|
||||
prefix?: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface CreateServiceTokenEvent {
|
||||
type: EventType.CREATE_SERVICE_TOKEN;
|
||||
metadata: {
|
||||
name: string;
|
||||
scopes: Array<{
|
||||
environment: string;
|
||||
secretPath: string;
|
||||
}>;
|
||||
};
|
||||
}
|
||||
|
||||
interface DeleteServiceTokenEvent {
|
||||
type: EventType.DELETE_SERVICE_TOKEN;
|
||||
metadata: {
|
||||
name: string;
|
||||
scopes: Array<{
|
||||
environment: string;
|
||||
secretPath: string;
|
||||
}>;
|
||||
};
|
||||
}
|
||||
|
||||
interface CreateIdentityEvent {
|
||||
// note: currently not logging org-role
|
||||
type: EventType.CREATE_IDENTITY;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
name: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface UpdateIdentityEvent {
|
||||
type: EventType.UPDATE_IDENTITY;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
name?: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface DeleteIdentityEvent {
|
||||
type: EventType.DELETE_IDENTITY;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface LoginIdentityUniversalAuthEvent {
|
||||
type: EventType.LOGIN_IDENTITY_UNIVERSAL_AUTH;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
identityUniversalAuthId: string;
|
||||
clientSecretId: string;
|
||||
identityAccessTokenId: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface AddIdentityUniversalAuthEvent {
|
||||
type: EventType.ADD_IDENTITY_UNIVERSAL_AUTH;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
clientSecretTrustedIps: Array<TIdentityTrustedIp>;
|
||||
accessTokenTTL: number;
|
||||
accessTokenMaxTTL: number;
|
||||
accessTokenNumUsesLimit: number;
|
||||
accessTokenTrustedIps: Array<TIdentityTrustedIp>;
|
||||
};
|
||||
}
|
||||
|
||||
interface UpdateIdentityUniversalAuthEvent {
|
||||
type: EventType.UPDATE_IDENTITY_UNIVERSAL_AUTH;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
clientSecretTrustedIps?: Array<TIdentityTrustedIp>;
|
||||
accessTokenTTL?: number;
|
||||
accessTokenMaxTTL?: number;
|
||||
accessTokenNumUsesLimit?: number;
|
||||
accessTokenTrustedIps?: Array<TIdentityTrustedIp>;
|
||||
};
|
||||
}
|
||||
|
||||
interface GetIdentityUniversalAuthEvent {
|
||||
type: EventType.GET_IDENTITY_UNIVERSAL_AUTH;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface CreateIdentityUniversalAuthClientSecretEvent {
|
||||
type: EventType.CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
clientSecretId: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface GetIdentityUniversalAuthClientSecretsEvent {
|
||||
type: EventType.GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRETS;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface RevokeIdentityUniversalAuthClientSecretEvent {
|
||||
type: EventType.REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
clientSecretId: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface CreateEnvironmentEvent {
|
||||
type: EventType.CREATE_ENVIRONMENT;
|
||||
metadata: {
|
||||
name: string;
|
||||
slug: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface UpdateEnvironmentEvent {
|
||||
type: EventType.UPDATE_ENVIRONMENT;
|
||||
metadata: {
|
||||
oldName: string;
|
||||
newName: string;
|
||||
oldSlug: string;
|
||||
newSlug: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface DeleteEnvironmentEvent {
|
||||
type: EventType.DELETE_ENVIRONMENT;
|
||||
metadata: {
|
||||
name: string;
|
||||
slug: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface AddWorkspaceMemberEvent {
|
||||
type: EventType.ADD_WORKSPACE_MEMBER;
|
||||
metadata: {
|
||||
userId: string;
|
||||
email: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface AddBatchWorkspaceMemberEvent {
|
||||
type: EventType.ADD_BATCH_WORKSPACE_MEMBER;
|
||||
metadata: Array<{
|
||||
userId: string;
|
||||
email: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
interface RemoveWorkspaceMemberEvent {
|
||||
type: EventType.REMOVE_WORKSPACE_MEMBER;
|
||||
metadata: {
|
||||
userId: string;
|
||||
email: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface CreateFolderEvent {
|
||||
type: EventType.CREATE_FOLDER;
|
||||
metadata: {
|
||||
environment: string;
|
||||
folderId: string;
|
||||
folderName: string;
|
||||
folderPath: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface UpdateFolderEvent {
|
||||
type: EventType.UPDATE_FOLDER;
|
||||
metadata: {
|
||||
environment: string;
|
||||
folderId: string;
|
||||
oldFolderName: string;
|
||||
newFolderName: string;
|
||||
folderPath: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface DeleteFolderEvent {
|
||||
type: EventType.DELETE_FOLDER;
|
||||
metadata: {
|
||||
environment: string;
|
||||
folderId: string;
|
||||
folderName: string;
|
||||
folderPath: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface CreateWebhookEvent {
|
||||
type: EventType.CREATE_WEBHOOK;
|
||||
metadata: {
|
||||
webhookId: string;
|
||||
environment: string;
|
||||
secretPath: string;
|
||||
webhookUrl: string;
|
||||
isDisabled: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface UpdateWebhookStatusEvent {
|
||||
type: EventType.UPDATE_WEBHOOK_STATUS;
|
||||
metadata: {
|
||||
webhookId: string;
|
||||
environment: string;
|
||||
secretPath: string;
|
||||
webhookUrl: string;
|
||||
isDisabled: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface DeleteWebhookEvent {
|
||||
type: EventType.DELETE_WEBHOOK;
|
||||
metadata: {
|
||||
webhookId: string;
|
||||
environment: string;
|
||||
secretPath: string;
|
||||
webhookUrl: string;
|
||||
isDisabled: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface GetSecretImportsEvent {
|
||||
type: EventType.GET_SECRET_IMPORTS;
|
||||
metadata: {
|
||||
environment: string;
|
||||
folderId: string;
|
||||
numberOfImports: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface CreateSecretImportEvent {
|
||||
type: EventType.CREATE_SECRET_IMPORT;
|
||||
metadata: {
|
||||
secretImportId: string;
|
||||
folderId: string;
|
||||
importFromEnvironment: string;
|
||||
importFromSecretPath: string;
|
||||
importToEnvironment: string;
|
||||
importToSecretPath: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface UpdateSecretImportEvent {
|
||||
type: EventType.UPDATE_SECRET_IMPORT;
|
||||
metadata: {
|
||||
secretImportId: string;
|
||||
folderId: string;
|
||||
importToEnvironment: string;
|
||||
importToSecretPath: string;
|
||||
position: number;
|
||||
orderBefore?: {
|
||||
environment: string;
|
||||
secretPath: string;
|
||||
}[];
|
||||
orderAfter?: {
|
||||
environment: string;
|
||||
secretPath: string;
|
||||
}[];
|
||||
};
|
||||
}
|
||||
|
||||
interface DeleteSecretImportEvent {
|
||||
type: EventType.DELETE_SECRET_IMPORT;
|
||||
metadata: {
|
||||
secretImportId: string;
|
||||
folderId: string;
|
||||
importFromEnvironment: string;
|
||||
importFromSecretPath: string;
|
||||
importToEnvironment: string;
|
||||
importToSecretPath: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface UpdateUserRole {
|
||||
type: EventType.UPDATE_USER_WORKSPACE_ROLE;
|
||||
metadata: {
|
||||
userId: string;
|
||||
email: string;
|
||||
oldRole: string;
|
||||
newRole: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface UpdateUserDeniedPermissions {
|
||||
type: EventType.UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS;
|
||||
metadata: {
|
||||
userId: string;
|
||||
email: string;
|
||||
deniedPermissions: {
|
||||
environmentSlug: string;
|
||||
ability: string;
|
||||
}[];
|
||||
};
|
||||
}
|
||||
interface SecretApprovalMerge {
|
||||
type: EventType.SECRET_APPROVAL_MERGED;
|
||||
metadata: {
|
||||
mergedBy: string;
|
||||
secretApprovalRequestSlug: string;
|
||||
secretApprovalRequestId: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface SecretApprovalClosed {
|
||||
type: EventType.SECRET_APPROVAL_CLOSED;
|
||||
metadata: {
|
||||
closedBy: string;
|
||||
secretApprovalRequestSlug: string;
|
||||
secretApprovalRequestId: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface SecretApprovalReopened {
|
||||
type: EventType.SECRET_APPROVAL_REOPENED;
|
||||
metadata: {
|
||||
reopenedBy: string;
|
||||
secretApprovalRequestSlug: string;
|
||||
secretApprovalRequestId: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface SecretApprovalRequest {
|
||||
type: EventType.SECRET_APPROVAL_REQUEST;
|
||||
metadata: {
|
||||
committedBy: string;
|
||||
secretApprovalRequestSlug: string;
|
||||
secretApprovalRequestId: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type Event =
|
||||
| GetSecretsEvent
|
||||
| GetSecretEvent
|
||||
| CreateSecretEvent
|
||||
| CreateSecretBatchEvent
|
||||
| UpdateSecretEvent
|
||||
| UpdateSecretBatchEvent
|
||||
| DeleteSecretEvent
|
||||
| DeleteSecretBatchEvent
|
||||
| GetWorkspaceKeyEvent
|
||||
| AuthorizeIntegrationEvent
|
||||
| UnauthorizeIntegrationEvent
|
||||
| CreateIntegrationEvent
|
||||
| DeleteIntegrationEvent
|
||||
| AddTrustedIPEvent
|
||||
| UpdateTrustedIPEvent
|
||||
| DeleteTrustedIPEvent
|
||||
| CreateServiceTokenEvent
|
||||
| DeleteServiceTokenEvent
|
||||
| CreateIdentityEvent
|
||||
| UpdateIdentityEvent
|
||||
| DeleteIdentityEvent
|
||||
| LoginIdentityUniversalAuthEvent
|
||||
| AddIdentityUniversalAuthEvent
|
||||
| UpdateIdentityUniversalAuthEvent
|
||||
| GetIdentityUniversalAuthEvent
|
||||
| CreateIdentityUniversalAuthClientSecretEvent
|
||||
| GetIdentityUniversalAuthClientSecretsEvent
|
||||
| RevokeIdentityUniversalAuthClientSecretEvent
|
||||
| CreateEnvironmentEvent
|
||||
| UpdateEnvironmentEvent
|
||||
| DeleteEnvironmentEvent
|
||||
| AddWorkspaceMemberEvent
|
||||
| AddBatchWorkspaceMemberEvent
|
||||
| RemoveWorkspaceMemberEvent
|
||||
| CreateFolderEvent
|
||||
| UpdateFolderEvent
|
||||
| DeleteFolderEvent
|
||||
| CreateWebhookEvent
|
||||
| UpdateWebhookStatusEvent
|
||||
| DeleteWebhookEvent
|
||||
| GetSecretImportsEvent
|
||||
| CreateSecretImportEvent
|
||||
| UpdateSecretImportEvent
|
||||
| DeleteSecretImportEvent
|
||||
| UpdateUserRole
|
||||
| UpdateUserDeniedPermissions
|
||||
| SecretApprovalMerge
|
||||
| SecretApprovalClosed
|
||||
| SecretApprovalRequest
|
||||
| SecretApprovalReopened;
|
||||
10
backend-pg/src/ee/services/saml-config/saml-config-dal.ts
Normal file
10
backend-pg/src/ee/services/saml-config/saml-config-dal.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { TDbClient } from "@app/db";
|
||||
import { TableName } from "@app/db/schemas";
|
||||
import { ormify } from "@app/lib/knex";
|
||||
|
||||
export type TSamlConfigDalFactory = ReturnType<typeof samlConfigDalFactory>;
|
||||
|
||||
export const samlConfigDalFactory = (db: TDbClient) => {
|
||||
const samlCfgOrm = ormify(db, TableName.SamlConfig);
|
||||
return samlCfgOrm;
|
||||
};
|
||||
348
backend-pg/src/ee/services/saml-config/saml-config-service.ts
Normal file
348
backend-pg/src/ee/services/saml-config/saml-config-service.ts
Normal file
@@ -0,0 +1,348 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import jwt from "jsonwebtoken";
|
||||
|
||||
import {
|
||||
OrgMembershipRole,
|
||||
OrgMembershipStatus,
|
||||
SecretKeyEncoding,
|
||||
TSamlConfigs,
|
||||
TSamlConfigsUpdate
|
||||
} from "@app/db/schemas";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import {
|
||||
decryptSymmetric,
|
||||
encryptSymmetric,
|
||||
infisicalSymmetricDecrypt
|
||||
} from "@app/lib/crypto/encryption";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { AuthTokenType } from "@app/services/auth/auth-type";
|
||||
import { TOrgBotDalFactory } from "@app/services/org/org-bot-dal";
|
||||
import { TOrgDalFactory } from "@app/services/org/org-dal";
|
||||
import { TUserDalFactory } from "@app/services/user/user-dal";
|
||||
|
||||
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||
import { TSamlConfigDalFactory } from "./saml-config-dal";
|
||||
import {
|
||||
SamlProviders,
|
||||
TCreateSamlCfgDTO,
|
||||
TGetSamlCfgDTO,
|
||||
TSamlLoginDTO,
|
||||
TUpdateSamlCfgDTO
|
||||
} from "./saml-config-types";
|
||||
|
||||
type TSamlConfigServiceFactoryDep = {
|
||||
samlConfigDal: TSamlConfigDalFactory;
|
||||
userDal: Pick<TUserDalFactory, "create" | "findUserByEmail" | "transaction" | "updateById">;
|
||||
orgDal: Pick<
|
||||
TOrgDalFactory,
|
||||
"createMembership" | "updateMembershipById" | "findMembership" | "findOrgById"
|
||||
>;
|
||||
orgBotDal: Pick<TOrgBotDalFactory, "findOne">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
};
|
||||
|
||||
export type TSamlConfigServiceFactory = ReturnType<typeof samlConfigServiceFactory>;
|
||||
|
||||
export const samlConfigServiceFactory = ({
|
||||
samlConfigDal,
|
||||
orgBotDal,
|
||||
orgDal,
|
||||
userDal,
|
||||
permissionService
|
||||
}: TSamlConfigServiceFactoryDep) => {
|
||||
const createSamlCfg = async ({
|
||||
cert,
|
||||
actor,
|
||||
orgId,
|
||||
issuer,
|
||||
actorId,
|
||||
isActive,
|
||||
entryPoint,
|
||||
authProvider
|
||||
}: TCreateSamlCfgDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Create,
|
||||
OrgPermissionSubjects.Sso
|
||||
);
|
||||
|
||||
// TODO(akhilmhdh-pg): licence check
|
||||
|
||||
const orgBot = await orgBotDal.findOne({ orgId });
|
||||
if (!orgBot)
|
||||
throw new BadRequestError({ message: "Org bot not found", name: "OrgBotNotFound" });
|
||||
const key = infisicalSymmetricDecrypt({
|
||||
ciphertext: orgBot.encryptedSymmetricKey,
|
||||
iv: orgBot.symmetricKeyIV,
|
||||
tag: orgBot.symmetricKeyTag,
|
||||
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
|
||||
});
|
||||
|
||||
const {
|
||||
ciphertext: encryptedEntryPoint,
|
||||
iv: entryPointIV,
|
||||
tag: entryPointTag
|
||||
} = encryptSymmetric(entryPoint, key);
|
||||
const {
|
||||
ciphertext: encryptedIssuer,
|
||||
iv: issuerIV,
|
||||
tag: issuerTag
|
||||
} = encryptSymmetric(issuer, key);
|
||||
|
||||
const { ciphertext: encryptedCert, iv: certIV, tag: certTag } = encryptSymmetric(cert, key);
|
||||
const samlConfig = await samlConfigDal.create({
|
||||
orgId,
|
||||
authProvider,
|
||||
isActive,
|
||||
encryptedEntryPoint,
|
||||
entryPointIV,
|
||||
entryPointTag,
|
||||
encryptedIssuer,
|
||||
issuerIV,
|
||||
issuerTag,
|
||||
encryptedCert,
|
||||
certIV,
|
||||
certTag
|
||||
});
|
||||
return samlConfig;
|
||||
};
|
||||
|
||||
const updateSamlCfg = async ({
|
||||
orgId,
|
||||
actor,
|
||||
cert,
|
||||
actorId,
|
||||
issuer,
|
||||
isActive,
|
||||
entryPoint,
|
||||
authProvider
|
||||
}: TUpdateSamlCfgDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Edit,
|
||||
OrgPermissionSubjects.Sso
|
||||
);
|
||||
const updateQuery: TSamlConfigsUpdate = { authProvider, isActive };
|
||||
const orgBot = await orgBotDal.findOne({ orgId });
|
||||
if (!orgBot)
|
||||
throw new BadRequestError({ message: "Org bot not found", name: "OrgBotNotFound" });
|
||||
const key = infisicalSymmetricDecrypt({
|
||||
ciphertext: orgBot.encryptedSymmetricKey,
|
||||
iv: orgBot.symmetricKeyIV,
|
||||
tag: orgBot.symmetricKeyTag,
|
||||
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
|
||||
});
|
||||
|
||||
if (entryPoint) {
|
||||
const {
|
||||
ciphertext: encryptedEntryPoint,
|
||||
iv: entryPointIV,
|
||||
tag: entryPointTag
|
||||
} = encryptSymmetric(entryPoint, key);
|
||||
updateQuery.encryptedEntryPoint = encryptedEntryPoint;
|
||||
updateQuery.entryPointIV = entryPointIV;
|
||||
updateQuery.entryPointTag = entryPointTag;
|
||||
}
|
||||
if (issuer) {
|
||||
const {
|
||||
ciphertext: encryptedIssuer,
|
||||
iv: issuerIV,
|
||||
tag: issuerTag
|
||||
} = encryptSymmetric(issuer, key);
|
||||
updateQuery.encryptedIssuer = encryptedIssuer;
|
||||
updateQuery.issuerIV = issuerIV;
|
||||
updateQuery.issuerTag = issuerTag;
|
||||
}
|
||||
if (cert) {
|
||||
const { ciphertext: encryptedCert, iv: certIV, tag: certTag } = encryptSymmetric(cert, key);
|
||||
updateQuery.encryptedCert = encryptedCert;
|
||||
updateQuery.certIV = certIV;
|
||||
updateQuery.certTag = certTag;
|
||||
}
|
||||
const [ssoConfig] = await samlConfigDal.update({ orgId }, updateQuery);
|
||||
return ssoConfig;
|
||||
};
|
||||
|
||||
const getSaml = async (dto: TGetSamlCfgDTO) => {
|
||||
let ssoConfig: TSamlConfigs | undefined;
|
||||
if (dto.type === "org") {
|
||||
ssoConfig = await samlConfigDal.findOne({ orgId: dto.orgId });
|
||||
if (!ssoConfig) return;
|
||||
} else if (dto.type === "ssoId") {
|
||||
ssoConfig = await samlConfigDal.findById(dto.id);
|
||||
}
|
||||
if (!ssoConfig) throw new BadRequestError({ message: "Failed to find organization SSO data" });
|
||||
|
||||
// when dto is type id means it's internally used
|
||||
if (dto.type === "org") {
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
dto.actor,
|
||||
dto.actorId,
|
||||
ssoConfig!.orgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.Sso
|
||||
);
|
||||
}
|
||||
const {
|
||||
entryPointTag,
|
||||
entryPointIV,
|
||||
encryptedEntryPoint,
|
||||
certTag,
|
||||
certIV,
|
||||
encryptedCert,
|
||||
issuerTag,
|
||||
issuerIV,
|
||||
encryptedIssuer
|
||||
} = ssoConfig;
|
||||
|
||||
const orgBot = await orgBotDal.findOne({ orgId: ssoConfig.orgId });
|
||||
if (!orgBot)
|
||||
throw new BadRequestError({ message: "Org bot not found", name: "OrgBotNotFound" });
|
||||
const key = infisicalSymmetricDecrypt({
|
||||
ciphertext: orgBot.encryptedSymmetricKey,
|
||||
iv: orgBot.symmetricKeyIV,
|
||||
tag: orgBot.symmetricKeyTag,
|
||||
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
|
||||
});
|
||||
|
||||
let entryPoint = "";
|
||||
if (encryptedEntryPoint && entryPointIV && entryPointTag) {
|
||||
entryPoint = decryptSymmetric({
|
||||
ciphertext: encryptedEntryPoint,
|
||||
key,
|
||||
tag: entryPointTag,
|
||||
iv: entryPointIV
|
||||
});
|
||||
}
|
||||
|
||||
let issuer = "";
|
||||
if (encryptedIssuer && issuerTag && issuerIV) {
|
||||
issuer = decryptSymmetric({
|
||||
key,
|
||||
tag: issuerTag,
|
||||
iv: issuerIV,
|
||||
ciphertext: encryptedIssuer
|
||||
});
|
||||
}
|
||||
|
||||
let cert = "";
|
||||
if (encryptedCert && certTag && certIV) {
|
||||
cert = decryptSymmetric({ key, tag: certTag, iv: certIV, ciphertext: encryptedCert });
|
||||
}
|
||||
|
||||
return {
|
||||
id: ssoConfig.id,
|
||||
organization: ssoConfig.orgId,
|
||||
orgId: ssoConfig.orgId,
|
||||
authProvider: ssoConfig.authProvider,
|
||||
isActive: ssoConfig.isActive,
|
||||
entryPoint,
|
||||
issuer,
|
||||
cert
|
||||
};
|
||||
};
|
||||
|
||||
const samlLogin = async ({
|
||||
firstName,
|
||||
email,
|
||||
lastName,
|
||||
authProvider,
|
||||
orgId,
|
||||
relayState,
|
||||
isSignupAllowed
|
||||
}: TSamlLoginDTO) => {
|
||||
const appCfg = getConfig();
|
||||
let user = await userDal.findUserByEmail(email);
|
||||
const isSamlSignUpDisabled = !isSignupAllowed && !user;
|
||||
if (isSamlSignUpDisabled)
|
||||
throw new BadRequestError({ message: "User signup disabled", name: "Saml SSO login" });
|
||||
|
||||
const organization = await orgDal.findOrgById(orgId);
|
||||
if (!organization) throw new BadRequestError({ message: "Org not found" });
|
||||
|
||||
if (user) {
|
||||
const hasSamlEnabled = (user.authMethods || []).some((method) =>
|
||||
Object.values(SamlProviders).includes(method as SamlProviders)
|
||||
);
|
||||
await userDal.transaction(async (tx) => {
|
||||
if (!hasSamlEnabled) {
|
||||
await userDal.updateById(user.id, { authMethods: [authProvider] }, tx);
|
||||
}
|
||||
const [orgMembership] = await orgDal.findMembership({ userId: user.id, orgId }, { tx });
|
||||
if (!orgMembership) {
|
||||
await orgDal.createMembership(
|
||||
{
|
||||
userId: user.id,
|
||||
orgId,
|
||||
inviteEmail: email,
|
||||
role: OrgMembershipRole.Member,
|
||||
status: OrgMembershipStatus.Accepted
|
||||
},
|
||||
tx
|
||||
);
|
||||
} else if (orgMembership.status === OrgMembershipStatus.Invited) {
|
||||
await orgDal.updateMembershipById(
|
||||
orgMembership.id,
|
||||
{
|
||||
status: OrgMembershipStatus.Accepted
|
||||
},
|
||||
tx
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
user = await userDal.transaction(async (tx) => {
|
||||
const newUser = await userDal.create(
|
||||
{
|
||||
email,
|
||||
firstName,
|
||||
lastName,
|
||||
authMethods: [authProvider]
|
||||
},
|
||||
tx
|
||||
);
|
||||
await orgDal.createMembership({
|
||||
inviteEmail: email,
|
||||
orgId,
|
||||
role: OrgMembershipRole.Member,
|
||||
status: OrgMembershipStatus.Invited
|
||||
});
|
||||
return newUser;
|
||||
});
|
||||
}
|
||||
const isUserCompleted = Boolean(user.isAccepted);
|
||||
const providerAuthToken = jwt.sign(
|
||||
{
|
||||
authTokenType: AuthTokenType.PROVIDER_TOKEN,
|
||||
userId: user.id,
|
||||
email: user.email,
|
||||
firstName,
|
||||
lastName,
|
||||
organizationName: organization.name,
|
||||
organizationId: organization.id,
|
||||
authMethod: authProvider,
|
||||
isUserCompleted,
|
||||
...(relayState
|
||||
? {
|
||||
callbackPort: JSON.parse(relayState).callbackPort as string
|
||||
}
|
||||
: {})
|
||||
},
|
||||
appCfg.JWT_AUTH_SECRET,
|
||||
{
|
||||
expiresIn: appCfg.JWT_PROVIDER_AUTH_LIFETIME
|
||||
}
|
||||
);
|
||||
return { isUserCompleted, providerAuthToken };
|
||||
};
|
||||
|
||||
return {
|
||||
createSamlCfg,
|
||||
updateSamlCfg,
|
||||
getSaml,
|
||||
samlLogin
|
||||
};
|
||||
};
|
||||
43
backend-pg/src/ee/services/saml-config/saml-config-types.ts
Normal file
43
backend-pg/src/ee/services/saml-config/saml-config-types.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { TOrgPermission } from "@app/lib/types";
|
||||
import { ActorType } from "@app/services/auth/auth-type";
|
||||
|
||||
export enum SamlProviders {
|
||||
OKTA_SAML = "okta-saml",
|
||||
AZURE_SAML = "azure-saml",
|
||||
JUMPCLOUD_SAML = "jumpcloud-saml"
|
||||
}
|
||||
|
||||
export type TCreateSamlCfgDTO = {
|
||||
authProvider: SamlProviders;
|
||||
isActive: boolean;
|
||||
entryPoint: string;
|
||||
issuer: string;
|
||||
cert: string;
|
||||
} & TOrgPermission;
|
||||
|
||||
export type TUpdateSamlCfgDTO = Partial<{
|
||||
authProvider: SamlProviders;
|
||||
isActive: boolean;
|
||||
entryPoint: string;
|
||||
issuer: string;
|
||||
cert: string;
|
||||
}> &
|
||||
TOrgPermission;
|
||||
|
||||
export type TGetSamlCfgDTO =
|
||||
| { type: "org"; orgId: string; actor: ActorType; actorId: string }
|
||||
| {
|
||||
type: "ssoId";
|
||||
id: string;
|
||||
};
|
||||
|
||||
export type TSamlLoginDTO = {
|
||||
email: string;
|
||||
firstName: string;
|
||||
lastName?: string;
|
||||
authProvider: string;
|
||||
orgId: string;
|
||||
isSignupAllowed: boolean;
|
||||
// saml thingy
|
||||
relayState?: string;
|
||||
};
|
||||
@@ -84,12 +84,12 @@ export const secretApprovalRequestDalFactory = (db: TDbClient) => {
|
||||
childrenMapper: [
|
||||
{
|
||||
key: "reviewerMemberId",
|
||||
label: "reviewers",
|
||||
label: "reviewers" as const,
|
||||
mapper: ({ reviewerMemberId: member, reviewerStatus: status }) =>
|
||||
member ? { member, status } : undefined
|
||||
},
|
||||
{ key: "approverId", label: "approvers", mapper: ({ approverId }) => approverId }
|
||||
] as const
|
||||
{ key: "approverId", label: "approvers" as const, mapper: ({ approverId }) => approverId }
|
||||
]
|
||||
});
|
||||
if (!formatedDoc?.[0]) return;
|
||||
return {
|
||||
@@ -213,12 +213,12 @@ export const secretApprovalRequestDalFactory = (db: TDbClient) => {
|
||||
childrenMapper: [
|
||||
{
|
||||
key: "reviewerMemberId",
|
||||
label: "reviewers",
|
||||
label: "reviewers" as const,
|
||||
mapper: ({ reviewerMemberId: member, reviewerStatus: s }) =>
|
||||
member ? { member, status: s } : undefined
|
||||
},
|
||||
{ key: "approverId", label: "approvers", mapper: ({ approverId }) => approverId }
|
||||
] as const
|
||||
{ key: "approverId", label: "approvers" as const, mapper: ({ approverId }) => approverId }
|
||||
]
|
||||
});
|
||||
return formatedDoc.map((el) => ({
|
||||
...el,
|
||||
|
||||
@@ -181,7 +181,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
status,
|
||||
statusChangeBy: membership.id
|
||||
});
|
||||
return updatedRequest;
|
||||
return { ...secretApprovalRequest, ...updatedRequest };
|
||||
};
|
||||
|
||||
const mergeSecretApprovalRequest = async ({
|
||||
|
||||
@@ -61,7 +61,7 @@ export const secretRotationDalFactory = (db: TDbClient) => {
|
||||
childrenMapper: [
|
||||
{
|
||||
key: "secId",
|
||||
label: "outputs",
|
||||
label: "outputs" as const,
|
||||
mapper: ({
|
||||
secId,
|
||||
outputKey,
|
||||
@@ -92,7 +92,7 @@ export const secretRotationDalFactory = (db: TDbClient) => {
|
||||
}
|
||||
})
|
||||
}
|
||||
] as const
|
||||
]
|
||||
});
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "SecretRotationFind" });
|
||||
|
||||
@@ -7,7 +7,8 @@ import {
|
||||
TSecretFolderVersions,
|
||||
TSecretSnapshotFolders,
|
||||
TSecretSnapshots,
|
||||
TSecretVersions} from "@app/db/schemas";
|
||||
TSecretVersions
|
||||
} from "@app/db/schemas";
|
||||
import { DatabaseError } from "@app/lib/errors";
|
||||
import { ormify, selectAllTableCols, sqlNestRelationships } from "@app/lib/knex";
|
||||
|
||||
@@ -109,15 +110,15 @@ export const snapshotDalFactory = (db: TDbClient) => {
|
||||
childrenMapper: [
|
||||
{
|
||||
key: "id",
|
||||
label: "secretVersions",
|
||||
label: "secretVersions" as const,
|
||||
mapper: (el) => SecretVersionsSchema.parse(el)
|
||||
},
|
||||
{
|
||||
key: "folderVerId",
|
||||
label: "folderVersion",
|
||||
label: "folderVersion" as const,
|
||||
mapper: ({ folderVerId: id, folderVerName: name }) => ({ id, name })
|
||||
}
|
||||
] as const
|
||||
]
|
||||
})?.[0];
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "FindSecretSnapshotDataById" });
|
||||
@@ -237,7 +238,7 @@ export const snapshotDalFactory = (db: TDbClient) => {
|
||||
childrenMapper: [
|
||||
{
|
||||
key: "id",
|
||||
label: "secretVersions",
|
||||
label: "secretVersions" as const,
|
||||
mapper: (el) => ({
|
||||
...SecretVersionsSchema.parse(el),
|
||||
latestSecretVersion: el.latestSecretVersion
|
||||
@@ -245,14 +246,14 @@ export const snapshotDalFactory = (db: TDbClient) => {
|
||||
},
|
||||
{
|
||||
key: "folderVerId",
|
||||
label: "folderVersion",
|
||||
label: "folderVersion" as const,
|
||||
mapper: ({ folderVerId: id, folderVerName: name, latestFolderVersion }) => ({
|
||||
id,
|
||||
name,
|
||||
latestFolderVersion
|
||||
})
|
||||
}
|
||||
] as const
|
||||
]
|
||||
});
|
||||
return formated;
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
import dotenv from "dotenv";
|
||||
|
||||
import { initDbConnection } from "./db";
|
||||
import {
|
||||
SecretVersionsSchema,
|
||||
TableName,
|
||||
TSecretFolderVersions,
|
||||
TSecrets,
|
||||
TSecretSnapshotFolders,
|
||||
TSecretSnapshots,
|
||||
TSecretVersions} from "./db/schemas";
|
||||
import { selectAllTableCols, sqlNestRelationships } from "./lib/knex";
|
||||
|
||||
dotenv.config();
|
||||
// const db = initDbConnection(process.env.DB_CONNECTION_URI);
|
||||
|
||||
// const main = async () => {
|
||||
// const folders = db
|
||||
// .withRecursive("parent", (qb) => {
|
||||
// qb.select({
|
||||
// depth: 1,
|
||||
// path: db.raw("'/'")
|
||||
// })
|
||||
// .select(selectAllTableCols(db, TableName.SecretFolder))
|
||||
// .from(TableName.SecretFolder)
|
||||
// .join(
|
||||
// TableName.Environment,
|
||||
// `${TableName.SecretFolder}.envId`,
|
||||
// `${TableName.Environment}.id`
|
||||
// )
|
||||
// .where({
|
||||
// projectId: "01c10de1-8743-490f-9c8a-7a19c4dc72a9",
|
||||
// parentId: null
|
||||
// })
|
||||
// .where(`${TableName.Environment}.slug`, "dev")
|
||||
// .union((qb) =>
|
||||
// qb
|
||||
// .select({
|
||||
// depth: db.raw("parent.depth + 1"),
|
||||
// path: db.raw(
|
||||
// "CONCAT((CASE WHEN parent.path = '/' THEN '' ELSE parent.path END),'/', secret_folders.name)"
|
||||
// )
|
||||
// })
|
||||
// .select(selectAllTableCols(db, TableName.SecretFolder))
|
||||
// .whereRaw(
|
||||
// `depth = array_position(ARRAY[${[1, 2]
|
||||
// .map((_) => "?")
|
||||
// .join(",")}]::varchar[], secret_folders.name,depth)`,
|
||||
// [...["ui", "design"]]
|
||||
// )
|
||||
// .from(TableName.SecretFolder)
|
||||
// .join("parent", "parent.id", `${TableName.SecretFolder}.parentId`)
|
||||
// );
|
||||
// })
|
||||
// .select("*")
|
||||
// .from("parent")
|
||||
// .orderBy("depth", "desc")
|
||||
// .first();
|
||||
// console.log(folders.toSQL());
|
||||
// console.log(JSON.stringify(await folders, null, 4));
|
||||
// process.exit(0);
|
||||
// };
|
||||
//
|
||||
const main = async () => {
|
||||
const db = initDbConnection(process.env.DB_CONNECTION_URI);
|
||||
|
||||
const folders = db
|
||||
.withRecursive("parent", (qb) => {
|
||||
qb.from(TableName.Snapshot)
|
||||
.leftJoin<TSecretSnapshotFolders>(
|
||||
TableName.SnapshotFolder,
|
||||
`${TableName.SnapshotFolder}.snapshotId`,
|
||||
`${TableName.Snapshot}.id`
|
||||
)
|
||||
.leftJoin<TSecretFolderVersions>(
|
||||
TableName.SecretFolderVersion,
|
||||
`${TableName.SnapshotFolder}.folderVersionId`,
|
||||
`${TableName.SecretFolderVersion}.id`
|
||||
)
|
||||
.select(selectAllTableCols(TableName.Snapshot))
|
||||
.select({ depth: 1 })
|
||||
.select(
|
||||
db.ref("name").withSchema(TableName.SecretFolderVersion).as("folderVerName"),
|
||||
db.ref("folderId").withSchema(TableName.SecretFolderVersion).as("folderVerId")
|
||||
)
|
||||
.where(`${TableName.Snapshot}.id`, "abe694d3-957f-40f5-a907-952d3e5ccaf1")
|
||||
.union((cb) =>
|
||||
cb
|
||||
.select(selectAllTableCols(TableName.Snapshot))
|
||||
.select({ depth: db.raw("parent.depth + 1") })
|
||||
.select(
|
||||
db.ref("name").withSchema(TableName.SecretFolderVersion).as("folderVerName"),
|
||||
db.ref("folderId").withSchema(TableName.SecretFolderVersion).as("folderVerId")
|
||||
)
|
||||
.from(TableName.Snapshot)
|
||||
.join<TSecretSnapshots, TSecretSnapshots & { secretId: string; max: number }>(
|
||||
db(TableName.Snapshot)
|
||||
.groupBy("folderId")
|
||||
.max("createdAt")
|
||||
.select("folderId")
|
||||
.as("latestVersion"),
|
||||
`${TableName.Snapshot}.createdAt`,
|
||||
"latestVersion.max"
|
||||
)
|
||||
.leftJoin<TSecretSnapshotFolders>(
|
||||
TableName.SnapshotFolder,
|
||||
`${TableName.SnapshotFolder}.snapshotId`,
|
||||
`${TableName.Snapshot}.id`
|
||||
)
|
||||
.leftJoin<TSecretFolderVersions>(
|
||||
TableName.SecretFolderVersion,
|
||||
`${TableName.SnapshotFolder}.folderVersionId`,
|
||||
`${TableName.SecretFolderVersion}.id`
|
||||
)
|
||||
.join("parent", "parent.folderVerId", `${TableName.Snapshot}.folderId`)
|
||||
);
|
||||
})
|
||||
.orderBy("depth", "asc")
|
||||
.from<TSecretSnapshots & { folderVerId: string; folderVerName: string }>("parent")
|
||||
.leftJoin<TSecretSnapshots>(
|
||||
TableName.SnapshotSecret,
|
||||
`parent.id`,
|
||||
`${TableName.SnapshotSecret}.snapshotId`
|
||||
)
|
||||
.leftJoin<TSecretVersions>(
|
||||
TableName.SecretVersion,
|
||||
`${TableName.SnapshotSecret}.secretVersionId`,
|
||||
`${TableName.SecretVersion}.id`
|
||||
)
|
||||
.leftJoin<{ latestSecretVersion: number }>(
|
||||
db(TableName.SecretVersion)
|
||||
.groupBy("secretId")
|
||||
.select("secretId")
|
||||
.max("version")
|
||||
.as("secGroupByMaxVersion"),
|
||||
`${TableName.SecretVersion}.secretId`,
|
||||
"secGroupByMaxVersion.secretId"
|
||||
)
|
||||
.leftJoin<{ latestFolderVersion: number }>(
|
||||
db(TableName.SecretFolderVersion)
|
||||
.groupBy("folderId")
|
||||
.select("folderId")
|
||||
.max("version")
|
||||
.as("folderGroupByMaxVersion"),
|
||||
`parent.folderId`,
|
||||
"folderGroupByMaxVersion.folderId"
|
||||
)
|
||||
.select(selectAllTableCols(TableName.SecretVersion))
|
||||
.select(
|
||||
db.ref("id").withSchema("parent").as("snapshotId"),
|
||||
db.ref("folderId").withSchema("parent").as("snapshotFolderId"),
|
||||
db.ref("parentFolderId").withSchema("parent").as("snapshotParentFolderId"),
|
||||
db.ref("folderVerName").withSchema("parent"),
|
||||
db.ref("folderVerId").withSchema("parent"),
|
||||
db.ref("max").withSchema("secGroupByMaxVersion").as("latestSecretVersion"),
|
||||
db.ref("max").withSchema("folderGroupByMaxVersion").as("latestFolderVersion")
|
||||
);
|
||||
console.log(folders.toSQL());
|
||||
const data = await folders;
|
||||
// console.log(data);
|
||||
const formated = sqlNestRelationships({
|
||||
data,
|
||||
key: "snapshotId",
|
||||
parentMapper: ({ snapshotId: id }) => ({
|
||||
id
|
||||
}),
|
||||
childrenMapper: [
|
||||
{
|
||||
key: "id",
|
||||
label: "secretVersions",
|
||||
mapper: (el) => ({
|
||||
...SecretVersionsSchema.parse(el),
|
||||
latestSecretVersion: el.latestSecretVersion
|
||||
})
|
||||
},
|
||||
{
|
||||
key: "folderVerId",
|
||||
label: "folderVersion",
|
||||
mapper: ({ folderVerId: id, folderVerName: name, latestFolderVersion }) => ({
|
||||
id,
|
||||
name,
|
||||
version: latestFolderVersion
|
||||
})
|
||||
}
|
||||
] as const
|
||||
});
|
||||
console.log(formated);
|
||||
process.exit(0);
|
||||
};
|
||||
main();
|
||||
@@ -122,6 +122,8 @@ export const decryptAsymmetric = ({
|
||||
return naclUtils.encodeUTF8(plaintext);
|
||||
};
|
||||
|
||||
export const generateSymmetricKey = (size = 32) => crypto.randomBytes(size).toString("base64");
|
||||
|
||||
export const generateAsymmetricKeyPair = () => {
|
||||
const pair = nacl.box.keyPair();
|
||||
|
||||
|
||||
@@ -15,3 +15,23 @@ export const pick = <T extends object, TKeys extends keyof T>(
|
||||
{} as Pick<T, TKeys>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes (shakes out) undefined entries from an
|
||||
* object. Optional second argument shakes out values
|
||||
* by custom evaluation.
|
||||
*/
|
||||
export const shake = <RemovedKeys extends string, T = {}>(
|
||||
obj: T,
|
||||
filter: (value: any) => boolean = (x) => x === undefined || x === null
|
||||
): Omit<T, RemovedKeys> => {
|
||||
if (!obj) return {} as T;
|
||||
const keys = Object.keys(obj) as (keyof T)[];
|
||||
return keys.reduce((acc, key) => {
|
||||
if (filter(obj[key])) {
|
||||
return acc;
|
||||
}
|
||||
acc[key] = obj[key];
|
||||
return acc;
|
||||
}, {} as T);
|
||||
};
|
||||
|
||||
@@ -3,12 +3,12 @@ import { Tables } from "knex/types/tables";
|
||||
export const selectAllTableCols = <Tname extends keyof Tables>(tableName: Tname) =>
|
||||
`${tableName}.*` as keyof Tables[Tname]["base"];
|
||||
|
||||
export const stripUndefinedInWhere = <T extends object>(val: T) => {
|
||||
export const stripUndefinedInWhere = <T extends object>(val: T): Exclude<T, undefined> => {
|
||||
const copy = val;
|
||||
Object.entries(copy).forEach(([key, value]) => {
|
||||
if (typeof value === "undefined") {
|
||||
delete copy[key as keyof T];
|
||||
}
|
||||
});
|
||||
return copy;
|
||||
return copy as Exclude<T, undefined>;
|
||||
};
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import { Job, JobsOptions, Queue, Worker, WorkerListener } from "bullmq";
|
||||
import Redis from "ioredis";
|
||||
|
||||
import { TCreateAuditLogDTO } from "@app/ee/services/audit-log/audit-log-types";
|
||||
|
||||
export enum QueueName {
|
||||
SecretRotation = "secret-rotation"
|
||||
SecretRotation = "secret-rotation",
|
||||
AuditLog = "audit-log"
|
||||
}
|
||||
|
||||
export enum QueueJobs {
|
||||
SecretRotation = "secret-rotation-job"
|
||||
SecretRotation = "secret-rotation-job",
|
||||
AuditLog = "audit-log-job"
|
||||
}
|
||||
|
||||
export type TQueueJobTypes = {
|
||||
@@ -14,6 +18,10 @@ export type TQueueJobTypes = {
|
||||
payload: { rotationId: string };
|
||||
name: QueueJobs.SecretRotation;
|
||||
};
|
||||
[QueueName.AuditLog]: {
|
||||
name: QueueJobs.AuditLog;
|
||||
payload: TCreateAuditLogDTO;
|
||||
};
|
||||
};
|
||||
|
||||
export type TQueueServiceFactory = ReturnType<typeof queueServiceFactory>;
|
||||
@@ -65,7 +73,7 @@ export const queueServiceFactory = (redisUrl: string) => {
|
||||
name: T,
|
||||
job: TQueueJobTypes[T]["name"],
|
||||
data: TQueueJobTypes[T]["payload"],
|
||||
opts: JobsOptions & { jobId: string }
|
||||
opts: JobsOptions & { jobId?: string }
|
||||
) => {
|
||||
const q = queueContainer[name];
|
||||
await q.add(job, data, opts);
|
||||
|
||||
55
backend-pg/src/server/plugins/audit-log.ts
Normal file
55
backend-pg/src/server/plugins/audit-log.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import fp from "fastify-plugin";
|
||||
|
||||
import { UserAgentType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { ActorType } from "@app/services/auth/auth-type";
|
||||
|
||||
export const getUserAgentType = (userAgent: string | undefined) => {
|
||||
if (userAgent === undefined) {
|
||||
return UserAgentType.OTHER;
|
||||
}
|
||||
if (userAgent === UserAgentType.CLI) {
|
||||
return UserAgentType.CLI;
|
||||
}
|
||||
if (userAgent === UserAgentType.K8_OPERATOR) {
|
||||
return UserAgentType.K8_OPERATOR;
|
||||
}
|
||||
if (userAgent === UserAgentType.TERRAFORM) {
|
||||
return UserAgentType.TERRAFORM;
|
||||
}
|
||||
if (userAgent.toLowerCase().includes("mozilla")) {
|
||||
return UserAgentType.WEB;
|
||||
}
|
||||
if (userAgent.includes(UserAgentType.NODE_SDK)) {
|
||||
return UserAgentType.NODE_SDK;
|
||||
}
|
||||
if (userAgent.includes(UserAgentType.PYTHON_SDK)) {
|
||||
return UserAgentType.PYTHON_SDK;
|
||||
}
|
||||
return UserAgentType.OTHER;
|
||||
};
|
||||
|
||||
export const injectAuditLogInfo = fp(async (server: FastifyZodProvider) => {
|
||||
server.decorateRequest("auditLogInfo", null);
|
||||
server.addHook("onRequest", async (req) => {
|
||||
if (!req.auth) return;
|
||||
const userAgent = req.headers["user-agent"] ?? "";
|
||||
const payload = {
|
||||
ipAddress: req.realIp,
|
||||
userAgent,
|
||||
userAgentType: getUserAgentType(userAgent)
|
||||
} as typeof req.auditLogInfo;
|
||||
if (req.auth.actor === ActorType.USER) {
|
||||
payload.actor = {
|
||||
type: ActorType.USER,
|
||||
metadata: {
|
||||
email: req.auth.user.email,
|
||||
userId: req.auth.userId
|
||||
}
|
||||
};
|
||||
} else {
|
||||
throw new BadRequestError({ message: "Missing logic for other actor" });
|
||||
}
|
||||
req.auditLogInfo = payload;
|
||||
});
|
||||
});
|
||||
@@ -2,8 +2,13 @@ import { Knex } from "knex";
|
||||
import { z } from "zod";
|
||||
|
||||
import { registerV1EERoutes } from "@app/ee/routes/v1";
|
||||
import { auditLogDalFactory } from "@app/ee/services/audit-log/audit-log-dal";
|
||||
import { auditLogQueueServiceFactory } from "@app/ee/services/audit-log/audit-log-queue";
|
||||
import { auditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-service";
|
||||
import { permissionDalFactory } from "@app/ee/services/permission/permission-dal";
|
||||
import { permissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { samlConfigDalFactory } from "@app/ee/services/saml-config/saml-config-dal";
|
||||
import { samlConfigServiceFactory } from "@app/ee/services/saml-config/saml-config-service";
|
||||
import { sapApproverDalFactory } from "@app/ee/services/secret-approval-policy/sap-approver-dal";
|
||||
import { secretApprovalPolicyDalFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-dal";
|
||||
import { secretApprovalPolicyServiceFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-service";
|
||||
@@ -43,6 +48,7 @@ import { integrationServiceFactory } from "@app/services/integration/integration
|
||||
import { integrationAuthDalFactory } from "@app/services/integration-auth/integration-auth-dal";
|
||||
import { integrationAuthServiceFactory } from "@app/services/integration-auth/integration-auth-service";
|
||||
import { incidentContactDalFactory } from "@app/services/org/incident-contacts-dal";
|
||||
import { orgBotDalFactory } from "@app/services/org/org-bot-dal";
|
||||
import { orgDalFactory } from "@app/services/org/org-dal";
|
||||
import { orgRoleDalFactory } from "@app/services/org/org-role-dal";
|
||||
import { orgRoleServiceFactory } from "@app/services/org/org-role-service";
|
||||
@@ -80,6 +86,7 @@ import { userServiceFactory } from "@app/services/user/user-service";
|
||||
import { webhookDalFactory } from "@app/services/webhook/webhook-dal";
|
||||
import { webhookServiceFactory } from "@app/services/webhook/webhook-service";
|
||||
|
||||
import { injectAuditLogInfo } from "../plugins/audit-log";
|
||||
import { injectIdentity } from "../plugins/auth/inject-identity";
|
||||
import { injectPermission } from "../plugins/auth/inject-permission";
|
||||
import { registerV1Routes } from "./v1";
|
||||
@@ -99,6 +106,7 @@ export const registerRoutes = async (
|
||||
const authDal = authDalFactory(db);
|
||||
const authTokenDal = tokenDalFactory(db);
|
||||
const orgDal = orgDalFactory(db);
|
||||
const orgBotDal = orgBotDalFactory(db);
|
||||
const incidentContactDal = incidentContactDalFactory(db);
|
||||
const orgRoleDal = orgRoleDalFactory(db);
|
||||
const superAdminDal = superAdminDalFactory(db);
|
||||
@@ -132,8 +140,11 @@ export const registerRoutes = async (
|
||||
const identityUaDal = identityUaDalFactory(db);
|
||||
const identityUaClientSecretDal = identityUaClientSecretDalFactory(db);
|
||||
|
||||
const auditLogDal = auditLogDalFactory(db);
|
||||
|
||||
// ee db layer ops
|
||||
const permissionDal = permissionDalFactory(db);
|
||||
const samlConfigDal = samlConfigDalFactory(db);
|
||||
const sapApproverDal = sapApproverDalFactory(db);
|
||||
const secretApprovalPolicyDal = secretApprovalPolicyDalFactory(db);
|
||||
const secretApprovalRequestDal = secretApprovalRequestDalFactory(db);
|
||||
@@ -146,6 +157,8 @@ export const registerRoutes = async (
|
||||
const snapshotFolderDal = snapshotFolderDalFactory(db);
|
||||
|
||||
const permissionService = permissionServiceFactory({ permissionDal, orgRoleDal, projectRoleDal });
|
||||
const auditLogQueue = auditLogQueueServiceFactory({ auditLogDal, queueService });
|
||||
const auditLogService = auditLogServiceFactory({ auditLogDal, permissionService, auditLogQueue });
|
||||
const sapService = secretApprovalPolicyServiceFactory({
|
||||
projectMembershipDal,
|
||||
projectEnvDal,
|
||||
@@ -153,6 +166,13 @@ export const registerRoutes = async (
|
||||
permissionService,
|
||||
secretApprovalPolicyDal
|
||||
});
|
||||
const samlService = samlConfigServiceFactory({
|
||||
permissionService,
|
||||
orgBotDal,
|
||||
orgDal,
|
||||
userDal,
|
||||
samlConfigDal
|
||||
});
|
||||
const sarService = secretApprovalRequestServiceFactory({
|
||||
permissionService,
|
||||
folderDal,
|
||||
@@ -180,7 +200,8 @@ export const registerRoutes = async (
|
||||
incidentContactDal,
|
||||
tokenService,
|
||||
smtpService,
|
||||
userDal
|
||||
userDal,
|
||||
orgBotDal
|
||||
});
|
||||
const signupService = authSignupServiceFactory({
|
||||
tokenService,
|
||||
@@ -354,7 +375,9 @@ export const registerRoutes = async (
|
||||
secretApprovalPolicy: sapService,
|
||||
secretApprovalRequest: sarService,
|
||||
secretRotation: secretRotationService,
|
||||
snapshot: snapshotService
|
||||
snapshot: snapshotService,
|
||||
saml: samlService,
|
||||
auditLog: auditLogService
|
||||
});
|
||||
|
||||
server.decorate<FastifyZodProvider["store"]>("store", {
|
||||
@@ -363,6 +386,7 @@ export const registerRoutes = async (
|
||||
|
||||
await server.register(injectIdentity);
|
||||
await server.register(injectPermission);
|
||||
await server.register(injectAuditLogInfo);
|
||||
|
||||
server.route({
|
||||
url: "/status",
|
||||
@@ -394,9 +418,13 @@ export const registerRoutes = async (
|
||||
});
|
||||
|
||||
// register routes for v1
|
||||
await server.register(registerV1Routes, { prefix: "/v1" });
|
||||
await server.register(
|
||||
async (v1Server) => {
|
||||
await v1Server.register(registerV1EERoutes);
|
||||
await v1Server.register(registerV1Routes);
|
||||
},
|
||||
{ prefix: "/v1" }
|
||||
);
|
||||
await server.register(registerV2Routes, { prefix: "/v2" });
|
||||
await server.register(registerV3Routes, { prefix: "/v3" });
|
||||
|
||||
await server.register(registerV1EERoutes, { prefix: "/ee/v1" });
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { IdentitiesSchema, OrgMembershipRole } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@@ -28,6 +29,19 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
||||
...req.body,
|
||||
orgId: req.body.organizationId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: req.body.organizationId,
|
||||
event: {
|
||||
type: EventType.CREATE_IDENTITY,
|
||||
metadata: {
|
||||
name: identity.name,
|
||||
identityId: identity.id
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { identity };
|
||||
}
|
||||
});
|
||||
@@ -57,6 +71,19 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
||||
id: req.params.identityId,
|
||||
...req.body
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: identity.orgId,
|
||||
event: {
|
||||
type: EventType.UPDATE_IDENTITY,
|
||||
metadata: {
|
||||
name: identity.name,
|
||||
identityId: identity.id
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { identity };
|
||||
}
|
||||
});
|
||||
@@ -81,6 +108,17 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
||||
actorId: req.permission.id,
|
||||
id: req.params.identityId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: identity.orgId,
|
||||
event: {
|
||||
type: EventType.DELETE_IDENTITY,
|
||||
metadata: {
|
||||
identityId: identity.id
|
||||
}
|
||||
}
|
||||
});
|
||||
return { identity };
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { IdentityUaClientSecretsSchema, IdentityUniversalAuthsSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
|
||||
|
||||
export const sanitizedClientSecretSchema = IdentityUaClientSecretsSchema.pick({
|
||||
id: true,
|
||||
@@ -36,10 +38,21 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const { identityUa, accessToken } = await server.services.identityUa.login(
|
||||
req.body.clientId,
|
||||
req.body.clientSecret
|
||||
);
|
||||
const { identityUa, accessToken, identityAccessToken, validClientSecretInfo } =
|
||||
await server.services.identityUa.login(req.body.clientId, req.body.clientSecret);
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
event: {
|
||||
type: EventType.LOGIN_IDENTITY_UNIVERSAL_AUTH,
|
||||
metadata: {
|
||||
clientSecretId: validClientSecretInfo.id,
|
||||
identityId: identityUa.identityId,
|
||||
identityAccessTokenId: identityAccessToken.id,
|
||||
identityUniversalAuthId: identityUa.id
|
||||
}
|
||||
}
|
||||
});
|
||||
return {
|
||||
accessToken,
|
||||
tokenType: "Bearer" as const,
|
||||
@@ -102,6 +115,24 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
...req.body,
|
||||
identityId: req.params.identityId
|
||||
});
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: identityUniversalAuth.orgId,
|
||||
event: {
|
||||
type: EventType.ADD_IDENTITY_UNIVERSAL_AUTH,
|
||||
metadata: {
|
||||
identityId: identityUniversalAuth.identityId,
|
||||
accessTokenTTL: identityUniversalAuth.accessTokenTTL,
|
||||
accessTokenMaxTTL: identityUniversalAuth.accessTokenMaxTTL,
|
||||
accessTokenTrustedIps:
|
||||
identityUniversalAuth.accessTokenTrustedIps as TIdentityTrustedIp[],
|
||||
clientSecretTrustedIps:
|
||||
identityUniversalAuth.clientSecretTrustedIps as TIdentityTrustedIp[],
|
||||
accessTokenNumUsesLimit: identityUniversalAuth.accessTokenNumUsesLimit
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { identityUniversalAuth };
|
||||
}
|
||||
});
|
||||
@@ -152,6 +183,25 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
...req.body,
|
||||
identityId: req.params.identityId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: identityUniversalAuth.orgId,
|
||||
event: {
|
||||
type: EventType.UPDATE_IDENTITY_UNIVERSAL_AUTH,
|
||||
metadata: {
|
||||
identityId: identityUniversalAuth.identityId,
|
||||
accessTokenTTL: identityUniversalAuth.accessTokenTTL,
|
||||
accessTokenMaxTTL: identityUniversalAuth.accessTokenMaxTTL,
|
||||
accessTokenTrustedIps:
|
||||
identityUniversalAuth.accessTokenTrustedIps as TIdentityTrustedIp[],
|
||||
clientSecretTrustedIps:
|
||||
identityUniversalAuth.clientSecretTrustedIps as TIdentityTrustedIp[],
|
||||
accessTokenNumUsesLimit: identityUniversalAuth.accessTokenNumUsesLimit
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { identityUniversalAuth };
|
||||
}
|
||||
});
|
||||
@@ -176,6 +226,18 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
actorId: req.permission.id,
|
||||
identityId: req.params.identityId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: identityUniversalAuth.orgId,
|
||||
event: {
|
||||
type: EventType.GET_IDENTITY_UNIVERSAL_AUTH,
|
||||
metadata: {
|
||||
identityId: identityUniversalAuth.identityId
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { identityUniversalAuth };
|
||||
}
|
||||
});
|
||||
@@ -201,13 +263,26 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const { clientSecret, clientSecretData } =
|
||||
const { clientSecret, clientSecretData, orgId } =
|
||||
await server.services.identityUa.createUaClientSecret({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
identityId: req.params.identityId,
|
||||
...req.body
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId,
|
||||
event: {
|
||||
type: EventType.CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET,
|
||||
metadata: {
|
||||
identityId: req.params.identityId,
|
||||
clientSecretId: clientSecretData.id
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { clientSecret, clientSecretData };
|
||||
}
|
||||
});
|
||||
@@ -227,10 +302,22 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const clientSecretData = await server.services.identityUa.getUaClientSecrets({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
identityId: req.params.identityId
|
||||
const { clientSecrets: clientSecretData, orgId } =
|
||||
await server.services.identityUa.getUaClientSecrets({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
identityId: req.params.identityId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId,
|
||||
event: {
|
||||
type: EventType.GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRETS,
|
||||
metadata: {
|
||||
identityId: req.params.identityId
|
||||
}
|
||||
}
|
||||
});
|
||||
return { clientSecretData };
|
||||
}
|
||||
@@ -258,6 +345,19 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
identityId: req.params.identityId,
|
||||
clientSecretId: req.params.clientSecretId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: clientSecretData.orgId,
|
||||
event: {
|
||||
type: EventType.REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET,
|
||||
metadata: {
|
||||
identityId: clientSecretData.identityId,
|
||||
clientSecretId: clientSecretData.id
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { clientSecretData };
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@@ -77,6 +78,18 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
actor: req.permission.type,
|
||||
id: req.params.integrationAuthId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: integrationAuth.projectId,
|
||||
event: {
|
||||
type: EventType.UNAUTHORIZE_INTEGRATION,
|
||||
metadata: {
|
||||
integration: integrationAuth.integration
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { integrationAuth };
|
||||
}
|
||||
});
|
||||
@@ -85,6 +98,45 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
url: "/oauth-token",
|
||||
method: "POST",
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
body: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
code: z.string().trim(),
|
||||
integration: z.string().trim(),
|
||||
url: z.string().trim().url().optional()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
integrationAuth: integrationAuthPubSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const integrationAuth = await server.services.integrationAuth.oauthExchange({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
projectId: req.body.workspaceId,
|
||||
...req.body
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: req.body.workspaceId,
|
||||
event: {
|
||||
type: EventType.AUTHORIZE_INTEGRATION,
|
||||
metadata: {
|
||||
integration: integrationAuth.integration
|
||||
}
|
||||
}
|
||||
});
|
||||
return { integrationAuth };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/access-token",
|
||||
method: "POST",
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
body: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
@@ -108,6 +160,17 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
projectId: req.body.workspaceId,
|
||||
...req.body
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: req.body.workspaceId,
|
||||
event: {
|
||||
type: EventType.AUTHORIZE_INTEGRATION,
|
||||
metadata: {
|
||||
integration: integrationAuth.integration
|
||||
}
|
||||
}
|
||||
});
|
||||
return { integrationAuth };
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { IntegrationsSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { shake } from "@app/lib/fn";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@@ -45,11 +47,33 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const integration = await server.services.integration.createIntegration({
|
||||
const { integration, integrationAuth } = await server.services.integration.createIntegration({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
...req.body
|
||||
});
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: integrationAuth.projectId,
|
||||
event: {
|
||||
type: EventType.CREATE_INTEGRATION,
|
||||
metadata: shake({
|
||||
integrationId: integration.id.toString(),
|
||||
integration: integration.integration,
|
||||
environment: req.body.sourceEnvironment,
|
||||
secretPath: req.body.secretPath,
|
||||
url: integration.url,
|
||||
app: integration.app,
|
||||
appId: integration.appId,
|
||||
targetEnvironment: integration.targetEnvironment,
|
||||
targetEnvironmentId: integration.targetEnvironmentId,
|
||||
targetService: integration.targetService,
|
||||
targetServiceId: integration.targetServiceId,
|
||||
path: integration.path,
|
||||
region: integration.region
|
||||
}) as any
|
||||
}
|
||||
});
|
||||
return { integration };
|
||||
}
|
||||
});
|
||||
@@ -108,6 +132,29 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
||||
actor: req.permission.type,
|
||||
id: req.params.integrationId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: integration.projectId,
|
||||
event: {
|
||||
type: EventType.DELETE_INTEGRATION,
|
||||
metadata: shake({
|
||||
integrationId: integration.id,
|
||||
integration: integration.integration,
|
||||
environment: integration.environment.slug,
|
||||
secretPath: integration.secretPath,
|
||||
url: integration.url,
|
||||
app: integration.app,
|
||||
appId: integration.appId,
|
||||
targetEnvironment: integration.targetEnvironment,
|
||||
targetEnvironmentId: integration.targetEnvironmentId,
|
||||
targetService: integration.targetService,
|
||||
targetServiceId: integration.targetServiceId,
|
||||
path: integration.path,
|
||||
region: integration.region
|
||||
}) as any
|
||||
}
|
||||
});
|
||||
return { integration };
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { ProjectEnvironmentsSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@@ -32,6 +33,18 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
|
||||
projectId: req.params.workspaceId,
|
||||
...req.body
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: environment.projectId,
|
||||
event: {
|
||||
type: EventType.CREATE_ENVIRONMENT,
|
||||
metadata: {
|
||||
name: environment.name,
|
||||
slug: environment.slug
|
||||
}
|
||||
}
|
||||
});
|
||||
return {
|
||||
message: "Successfully created new environment",
|
||||
workspace: req.params.workspaceId,
|
||||
@@ -62,13 +75,28 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
|
||||
handler: async (req) => {
|
||||
const environment = await server.services.projectEnv.updateEnvironment({
|
||||
const { environment, old } = await server.services.projectEnv.updateEnvironment({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
projectId: req.params.workspaceId,
|
||||
id: req.params.id,
|
||||
...req.body
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: environment.projectId,
|
||||
event: {
|
||||
type: EventType.UPDATE_ENVIRONMENT,
|
||||
metadata: {
|
||||
oldName: old.name,
|
||||
oldSlug: old.slug,
|
||||
newName: old.name,
|
||||
newSlug: old.slug
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
message: "Successfully updated environment",
|
||||
workspace: req.params.workspaceId,
|
||||
@@ -101,6 +129,19 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
|
||||
projectId: req.params.workspaceId,
|
||||
id: req.params.id
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: environment.projectId,
|
||||
event: {
|
||||
type: EventType.DELETE_ENVIRONMENT,
|
||||
metadata: {
|
||||
slug: environment.slug,
|
||||
name: environment.name
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
message: "Successfully deleted environment",
|
||||
workspace: req.params.workspaceId,
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { ProjectMembershipsSchema, UserEncryptionKeysSchema, UsersSchema } from "@app/db/schemas";
|
||||
import {
|
||||
OrgMembershipsSchema,
|
||||
ProjectMembershipsSchema,
|
||||
UserEncryptionKeysSchema,
|
||||
UsersSchema
|
||||
} from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@@ -42,6 +48,55 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:workspaceId/memberships",
|
||||
method: "POST",
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
}),
|
||||
body: z.object({
|
||||
members: z
|
||||
.object({
|
||||
orgMembershipId: z.string().trim(),
|
||||
workspaceEncryptedKey: z.string().trim(),
|
||||
workspaceEncryptedNonce: z.string().trim()
|
||||
})
|
||||
.array()
|
||||
.min(1)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
success: z.boolean(),
|
||||
data: OrgMembershipsSchema.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const data = await server.services.projectMembership.addUsersToProject({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
projectId: req.params.workspaceId,
|
||||
members: req.body.members
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId: req.params.workspaceId,
|
||||
...req.auditLogInfo,
|
||||
event: {
|
||||
type: EventType.ADD_BATCH_WORKSPACE_MEMBER,
|
||||
metadata: data.map(({ userId }) => ({
|
||||
userId: userId || "",
|
||||
email: ""
|
||||
}))
|
||||
}
|
||||
});
|
||||
|
||||
return { data, success: true };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:workspaceId/memberships/:membershipId",
|
||||
method: "PATCH",
|
||||
@@ -68,6 +123,20 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
||||
membershipId: req.params.membershipId,
|
||||
role: req.body.role
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: req.params.workspaceId,
|
||||
event: {
|
||||
type: EventType.UPDATE_USER_WORKSPACE_ROLE,
|
||||
metadata: {
|
||||
userId: membership.userId,
|
||||
newRole: req.body.role,
|
||||
oldRole: membership.role,
|
||||
email: ""
|
||||
}
|
||||
}
|
||||
});
|
||||
return { membership };
|
||||
}
|
||||
});
|
||||
@@ -94,6 +163,18 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
||||
projectId: req.params.workspaceId,
|
||||
membershipId: req.params.membershipId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: req.params.workspaceId,
|
||||
event: {
|
||||
type: EventType.REMOVE_WORKSPACE_MEMBER,
|
||||
metadata: {
|
||||
userId: membership.userId,
|
||||
email: ""
|
||||
}
|
||||
}
|
||||
});
|
||||
return { membership };
|
||||
}
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
UserEncryptionKeysSchema,
|
||||
UsersSchema
|
||||
} from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@@ -265,6 +266,18 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
projectId: req.params.workspaceId,
|
||||
email: req.body.email
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: req.params.workspaceId,
|
||||
event: {
|
||||
type: EventType.ADD_WORKSPACE_MEMBER,
|
||||
metadata: {
|
||||
userId: invitee.id,
|
||||
email: invitee.email
|
||||
}
|
||||
}
|
||||
});
|
||||
return { invitee, latestKey };
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { SecretFoldersSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@@ -28,6 +29,19 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
actor: req.permission.type,
|
||||
...req.body
|
||||
});
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: req.body.projectId,
|
||||
event: {
|
||||
type: EventType.CREATE_FOLDER,
|
||||
metadata: {
|
||||
environment: req.body.environment,
|
||||
folderId: folder.id,
|
||||
folderName: folder.name,
|
||||
folderPath: req.body.path
|
||||
}
|
||||
}
|
||||
});
|
||||
return { folder };
|
||||
}
|
||||
});
|
||||
@@ -53,12 +67,26 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
|
||||
handler: async (req) => {
|
||||
const folder = await server.services.folder.updateFolder({
|
||||
const { folder, old } = await server.services.folder.updateFolder({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
...req.body,
|
||||
id: req.params.folderId
|
||||
});
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: req.body.projectId,
|
||||
event: {
|
||||
type: EventType.UPDATE_FOLDER,
|
||||
metadata: {
|
||||
environment: req.body.environment,
|
||||
folderId: folder.id,
|
||||
folderPath: req.body.path,
|
||||
newFolderName: folder.name,
|
||||
oldFolderName: old.name
|
||||
}
|
||||
}
|
||||
});
|
||||
return { folder };
|
||||
}
|
||||
});
|
||||
@@ -89,6 +117,19 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
...req.body,
|
||||
id: req.params.folderId
|
||||
});
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: req.body.projectId,
|
||||
event: {
|
||||
type: EventType.DELETE_FOLDER,
|
||||
metadata: {
|
||||
environment: req.body.environment,
|
||||
folderId: folder.id,
|
||||
folderPath: req.body.path,
|
||||
folderName: folder.name
|
||||
}
|
||||
}
|
||||
});
|
||||
return { folder };
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { SecretImportsSchema, SecretsSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@@ -37,6 +38,22 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
|
||||
...req.body,
|
||||
data: req.body.import
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: req.body.projectId,
|
||||
event: {
|
||||
type: EventType.CREATE_SECRET_IMPORT,
|
||||
metadata: {
|
||||
secretImportId: secretImport.id,
|
||||
folderId: secretImport.folderId,
|
||||
importFromSecretPath: secretImport.importPath,
|
||||
importFromEnvironment: secretImport.importEnv.slug,
|
||||
importToEnvironment: req.body.environment,
|
||||
importToSecretPath: req.body.path
|
||||
}
|
||||
}
|
||||
});
|
||||
return { message: "Successfully created secret import", secretImport };
|
||||
}
|
||||
});
|
||||
@@ -78,6 +95,22 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
|
||||
...req.body,
|
||||
data: req.body.import
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: req.body.projectId,
|
||||
event: {
|
||||
type: EventType.UPDATE_SECRET_IMPORT,
|
||||
metadata: {
|
||||
secretImportId: secretImport.id,
|
||||
folderId: secretImport.folderId,
|
||||
position: secretImport.position,
|
||||
importToEnvironment: req.body.environment,
|
||||
importToSecretPath: req.body.path
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { message: "Successfully updated secret import", secretImport };
|
||||
}
|
||||
});
|
||||
@@ -113,6 +146,22 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
|
||||
id: req.params.secretImportId,
|
||||
...req.body
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: req.body.projectId,
|
||||
event: {
|
||||
type: EventType.DELETE_SECRET_IMPORT,
|
||||
metadata: {
|
||||
secretImportId: secretImport.id,
|
||||
folderId: secretImport.folderId,
|
||||
importFromEnvironment: secretImport.importEnv.slug,
|
||||
importFromSecretPath: secretImport.importPath,
|
||||
importToEnvironment: req.body.environment,
|
||||
importToSecretPath: req.body.path
|
||||
}
|
||||
}
|
||||
});
|
||||
return { message: "Successfully deleted secret import", secretImport };
|
||||
}
|
||||
});
|
||||
@@ -146,6 +195,19 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
|
||||
actor: req.permission.type,
|
||||
...req.query
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: req.query.projectId,
|
||||
event: {
|
||||
type: EventType.GET_SECRET_IMPORTS,
|
||||
metadata: {
|
||||
environment: req.query.environment,
|
||||
folderId: secretImports?.[0]?.folderId,
|
||||
numberOfImports: secretImports.length
|
||||
}
|
||||
}
|
||||
});
|
||||
return { message: "Successfully fetched secret imports", secretImports };
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { WebhooksSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@@ -9,10 +10,10 @@ export const sanitizedWebhookSchema = WebhooksSchema.omit({
|
||||
iv: true,
|
||||
tag: true,
|
||||
algorithm: true,
|
||||
keyEncoding: true,
|
||||
keyEncoding: true
|
||||
}).merge(
|
||||
z.object({
|
||||
projectId:z.string(),
|
||||
projectId: z.string(),
|
||||
environment: z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
@@ -48,6 +49,22 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
|
||||
projectId: req.body.workspaceId,
|
||||
...req.body
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: req.body.workspaceId,
|
||||
event: {
|
||||
type: EventType.CREATE_WEBHOOK,
|
||||
metadata: {
|
||||
environment: webhook.environment.slug,
|
||||
webhookId: webhook.id,
|
||||
isDisabled: webhook.isDisabled,
|
||||
secretPath: webhook.secretPath,
|
||||
webhookUrl: webhook.url
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { message: "Successfully created webhook", webhook };
|
||||
}
|
||||
});
|
||||
@@ -77,6 +94,22 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
|
||||
id: req.params.webhookId,
|
||||
isDisabled: req.body.isDisabled
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: webhook.projectId,
|
||||
event: {
|
||||
type: EventType.UPDATE_WEBHOOK_STATUS,
|
||||
metadata: {
|
||||
environment: webhook.environment.slug,
|
||||
webhookId: webhook.id,
|
||||
isDisabled: webhook.isDisabled,
|
||||
secretPath: webhook.secretPath,
|
||||
webhookUrl: webhook.url
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { message: "Successfully updated webhook", webhook };
|
||||
}
|
||||
});
|
||||
@@ -96,6 +129,22 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
|
||||
actorId: req.permission.id,
|
||||
id: req.params.webhookId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: webhook.projectId,
|
||||
event: {
|
||||
type: EventType.DELETE_WEBHOOK,
|
||||
metadata: {
|
||||
environment: webhook.environment.slug,
|
||||
webhookId: webhook.id,
|
||||
isDisabled: webhook.isDisabled,
|
||||
secretPath: webhook.secretPath,
|
||||
webhookUrl: webhook.url
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { message: "Successfully deleted webhook", webhook };
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { ProjectKeysSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@@ -30,6 +31,17 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
projectId: req.params.workspaceId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: req.params.workspaceId,
|
||||
event: {
|
||||
type: EventType.GET_WORKSPACE_KEY,
|
||||
metadata: {
|
||||
keyId: key.id
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return key;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { ServiceTokensSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@@ -68,6 +69,18 @@ export const registerServiceTokenRouter = async (server: FastifyZodProvider) =>
|
||||
...req.body,
|
||||
projectId: req.body.workspaceId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: serviceToken.projectId,
|
||||
event: {
|
||||
type: EventType.CREATE_SERVICE_TOKEN,
|
||||
metadata: {
|
||||
name: serviceToken.name,
|
||||
scopes: req.body.scopes
|
||||
}
|
||||
}
|
||||
});
|
||||
return { serviceToken: token, serviceTokenData: serviceToken };
|
||||
}
|
||||
});
|
||||
@@ -92,6 +105,19 @@ export const registerServiceTokenRouter = async (server: FastifyZodProvider) =>
|
||||
actor: req.permission.type,
|
||||
id: req.params.serviceTokenId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: serviceTokenData.projectId,
|
||||
event: {
|
||||
type: EventType.DELETE_SERVICE_TOKEN,
|
||||
metadata: {
|
||||
name: serviceTokenData.name,
|
||||
scopes: serviceTokenData.scopes as Array<{ environment: string; secretPath: string }>
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { serviceTokenData };
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { SecretApprovalRequestsSchema, SecretsSchema, SecretType } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { CommitType } from "@app/ee/services/secret-approval-request/secret-approval-request-types";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
|
||||
@@ -35,6 +36,19 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
path: req.query.secretPath
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId: req.query.workspaceId,
|
||||
...req.auditLogInfo,
|
||||
event: {
|
||||
type: EventType.GET_SECRETS,
|
||||
metadata: {
|
||||
environment: req.query.environment,
|
||||
secretPath: req.query.secretPath,
|
||||
numberOfSecrets: secrets.length
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { secrets };
|
||||
}
|
||||
});
|
||||
@@ -74,6 +88,20 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
type: req.query.type
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId: req.query.workspaceId,
|
||||
...req.auditLogInfo,
|
||||
event: {
|
||||
type: EventType.GET_SECRET,
|
||||
metadata: {
|
||||
environment: req.query.environment,
|
||||
secretPath: req.query.secretPath,
|
||||
secretId: secret.id,
|
||||
secretKey: req.params.secretName,
|
||||
secretVersion: secret.version
|
||||
}
|
||||
}
|
||||
});
|
||||
return { secret };
|
||||
}
|
||||
});
|
||||
@@ -167,6 +195,19 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId: req.body.workspaceId,
|
||||
...req.auditLogInfo,
|
||||
event: {
|
||||
type: EventType.SECRET_APPROVAL_REQUEST,
|
||||
metadata: {
|
||||
committedBy: approval.committerId,
|
||||
secretApprovalRequestId: approval.id,
|
||||
secretApprovalRequestSlug: approval.slug
|
||||
}
|
||||
}
|
||||
});
|
||||
return { approval };
|
||||
}
|
||||
}
|
||||
@@ -191,6 +232,20 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
metadata
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId: req.body.workspaceId,
|
||||
...req.auditLogInfo,
|
||||
event: {
|
||||
type: EventType.CREATE_SECRET,
|
||||
metadata: {
|
||||
environment: req.body.environment,
|
||||
secretPath: req.body.secretPath,
|
||||
secretId: secret.id,
|
||||
secretKey: req.params.secretName,
|
||||
secretVersion: secret.version
|
||||
}
|
||||
}
|
||||
});
|
||||
return { secret };
|
||||
}
|
||||
});
|
||||
@@ -296,6 +351,19 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId: req.body.workspaceId,
|
||||
...req.auditLogInfo,
|
||||
event: {
|
||||
type: EventType.SECRET_APPROVAL_REQUEST,
|
||||
metadata: {
|
||||
committedBy: approval.committerId,
|
||||
secretApprovalRequestId: approval.id,
|
||||
secretApprovalRequestSlug: approval.slug
|
||||
}
|
||||
}
|
||||
});
|
||||
return { approval };
|
||||
}
|
||||
}
|
||||
@@ -325,6 +393,20 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
newSecretName
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId: req.body.workspaceId,
|
||||
...req.auditLogInfo,
|
||||
event: {
|
||||
type: EventType.UPDATE_SECRET,
|
||||
metadata: {
|
||||
environment: req.body.environment,
|
||||
secretPath: req.body.secretPath,
|
||||
secretId: secret.id,
|
||||
secretKey: req.params.secretName,
|
||||
secretVersion: secret.version
|
||||
}
|
||||
}
|
||||
});
|
||||
return { secret };
|
||||
}
|
||||
});
|
||||
@@ -382,6 +464,19 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId: req.body.workspaceId,
|
||||
...req.auditLogInfo,
|
||||
event: {
|
||||
type: EventType.SECRET_APPROVAL_REQUEST,
|
||||
metadata: {
|
||||
committedBy: approval.committerId,
|
||||
secretApprovalRequestId: approval.id,
|
||||
secretApprovalRequestSlug: approval.slug
|
||||
}
|
||||
}
|
||||
});
|
||||
return { approval };
|
||||
}
|
||||
}
|
||||
@@ -397,6 +492,20 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
secretId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId: req.body.workspaceId,
|
||||
...req.auditLogInfo,
|
||||
event: {
|
||||
type: EventType.DELETE_SECRET,
|
||||
metadata: {
|
||||
environment: req.body.environment,
|
||||
secretPath: req.body.secretPath,
|
||||
secretId: secret.id,
|
||||
secretKey: req.params.secretName,
|
||||
secretVersion: secret.version
|
||||
}
|
||||
}
|
||||
});
|
||||
return { secret };
|
||||
}
|
||||
});
|
||||
@@ -463,6 +572,19 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
[CommitType.Create]: inputSecrets.filter(({ type }) => type === "shared")
|
||||
}
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId: req.body.workspaceId,
|
||||
...req.auditLogInfo,
|
||||
event: {
|
||||
type: EventType.SECRET_APPROVAL_REQUEST,
|
||||
metadata: {
|
||||
committedBy: approval.committerId,
|
||||
secretApprovalRequestId: approval.id,
|
||||
secretApprovalRequestSlug: approval.slug
|
||||
}
|
||||
}
|
||||
});
|
||||
return { approval };
|
||||
}
|
||||
}
|
||||
@@ -476,6 +598,22 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
secrets: inputSecrets
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId: req.body.workspaceId,
|
||||
...req.auditLogInfo,
|
||||
event: {
|
||||
type: EventType.CREATE_SECRETS,
|
||||
metadata: {
|
||||
environment: req.body.environment,
|
||||
secretPath: req.body.secretPath,
|
||||
secrets: secrets.map((secret, i) => ({
|
||||
secretId: secret.id,
|
||||
secretKey: inputSecrets[i].secretName,
|
||||
secretVersion: secret.version
|
||||
}))
|
||||
}
|
||||
}
|
||||
});
|
||||
return { secrets };
|
||||
}
|
||||
});
|
||||
@@ -542,6 +680,19 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
[CommitType.Update]: inputSecrets.filter(({ type }) => type === "shared")
|
||||
}
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId: req.body.workspaceId,
|
||||
...req.auditLogInfo,
|
||||
event: {
|
||||
type: EventType.SECRET_APPROVAL_REQUEST,
|
||||
metadata: {
|
||||
committedBy: approval.committerId,
|
||||
secretApprovalRequestId: approval.id,
|
||||
secretApprovalRequestSlug: approval.slug
|
||||
}
|
||||
}
|
||||
});
|
||||
return { approval };
|
||||
}
|
||||
}
|
||||
@@ -554,6 +705,22 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
secrets: inputSecrets
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId: req.body.workspaceId,
|
||||
...req.auditLogInfo,
|
||||
event: {
|
||||
type: EventType.UPDATE_SECRETS,
|
||||
metadata: {
|
||||
environment: req.body.environment,
|
||||
secretPath: req.body.secretPath,
|
||||
secrets: secrets.map((secret, i) => ({
|
||||
secretId: secret.id,
|
||||
secretKey: inputSecrets[i].secretName,
|
||||
secretVersion: secret.version
|
||||
}))
|
||||
}
|
||||
}
|
||||
});
|
||||
return { secrets };
|
||||
}
|
||||
});
|
||||
@@ -609,6 +776,18 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
[CommitType.Delete]: inputSecrets.filter(({ type }) => type === "shared")
|
||||
}
|
||||
});
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId: req.body.workspaceId,
|
||||
...req.auditLogInfo,
|
||||
event: {
|
||||
type: EventType.SECRET_APPROVAL_REQUEST,
|
||||
metadata: {
|
||||
committedBy: approval.committerId,
|
||||
secretApprovalRequestId: approval.id,
|
||||
secretApprovalRequestSlug: approval.slug
|
||||
}
|
||||
}
|
||||
});
|
||||
return { approval };
|
||||
}
|
||||
}
|
||||
@@ -621,6 +800,22 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
secrets: inputSecrets
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId: req.body.workspaceId,
|
||||
...req.auditLogInfo,
|
||||
event: {
|
||||
type: EventType.DELETE_SECRETS,
|
||||
metadata: {
|
||||
environment: req.body.environment,
|
||||
secretPath: req.body.secretPath,
|
||||
secrets: secrets.map((secret, i) => ({
|
||||
secretId: secret.id,
|
||||
secretKey: inputSecrets[i].secretName,
|
||||
secretVersion: secret.version
|
||||
}))
|
||||
}
|
||||
}
|
||||
});
|
||||
return { secrets };
|
||||
}
|
||||
});
|
||||
|
||||
@@ -32,7 +32,8 @@ export enum AuthMode {
|
||||
export enum ActorType { // would extend to AWS, Azure, ...
|
||||
USER = "user", // userIdentity
|
||||
SERVICE = "service",
|
||||
IDENTITY = "identity"
|
||||
IDENTITY = "identity",
|
||||
Machine = "machine"
|
||||
}
|
||||
|
||||
export type AuthModeJwtTokenPayload = {
|
||||
|
||||
@@ -132,7 +132,7 @@ export const identityUaServiceFactory = ({
|
||||
: identityAccessToken.accessTokenMaxTTL
|
||||
}
|
||||
);
|
||||
return { accessToken, identityUa };
|
||||
return { accessToken, identityUa, validClientSecretInfo, identityAccessToken };
|
||||
};
|
||||
|
||||
const attachUa = async ({
|
||||
@@ -216,7 +216,7 @@ export const identityUaServiceFactory = ({
|
||||
);
|
||||
return doc;
|
||||
});
|
||||
return identityUa;
|
||||
return { ...identityUa, orgId: identityMembershipOrg.orgId };
|
||||
};
|
||||
|
||||
const updateUa = async ({
|
||||
@@ -295,7 +295,7 @@ export const identityUaServiceFactory = ({
|
||||
? JSON.stringify(reformattedAccessTokenTrustedIps)
|
||||
: undefined
|
||||
});
|
||||
return updatedUaAuth;
|
||||
return { ...updatedUaAuth, orgId: identityMembershipOrg.orgId };
|
||||
};
|
||||
|
||||
const getIdentityUa = async ({ identityId, actorId, actor }: TGetUaDTO) => {
|
||||
@@ -317,7 +317,7 @@ export const identityUaServiceFactory = ({
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
return uaIdentityAuth;
|
||||
return { ...uaIdentityAuth, orgId: identityMembershipOrg.orgId };
|
||||
};
|
||||
|
||||
const createUaClientSecret = async ({
|
||||
@@ -375,7 +375,8 @@ export const identityUaServiceFactory = ({
|
||||
return {
|
||||
clientSecret,
|
||||
clientSecretData: identityUaClientSecret,
|
||||
uaAuth: identityUniversalAuth
|
||||
uaAuth: identityUniversalAuth,
|
||||
orgId: identityMembershipOrg.orgId
|
||||
};
|
||||
};
|
||||
|
||||
@@ -415,7 +416,7 @@ export const identityUaServiceFactory = ({
|
||||
identityUAId: identityUniversalAuth.id,
|
||||
isClientSecretRevoked: false
|
||||
});
|
||||
return clientSecrets;
|
||||
return { clientSecrets, orgId: identityMembershipOrg.orgId };
|
||||
};
|
||||
|
||||
const revokeUaClientSecret = async ({
|
||||
@@ -454,7 +455,7 @@ export const identityUaServiceFactory = ({
|
||||
const clientSecret = await identityUaClientSecretDal.updateById(clientSecretId, {
|
||||
isClientSecretRevoked: true
|
||||
});
|
||||
return clientSecret;
|
||||
return { ...clientSecret, identityId, orgId: identityMembershipOrg.orgId };
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -111,8 +111,7 @@ export const identityServiceFactory = ({
|
||||
return newIdentity;
|
||||
});
|
||||
|
||||
// TODO(akhilmhdh-pg): add audit log here
|
||||
return identity;
|
||||
return { ...identity, orgId: identityOrgMembership.orgId };
|
||||
};
|
||||
|
||||
const deleteIdentity = async ({ actorId, actor, id }: TDeleteIdentityDTO) => {
|
||||
@@ -139,7 +138,7 @@ export const identityServiceFactory = ({
|
||||
throw new ForbiddenRequestError({ message: "Failed to delete more privileged identity" });
|
||||
|
||||
const deletedIdentity = await identityDal.deleteById(id);
|
||||
return deletedIdentity;
|
||||
return { ...deletedIdentity, orgId: identityOrgMembership.orgId };
|
||||
};
|
||||
|
||||
const listOrgIdentities = async ({ orgId, actor, actorId }: TOrgPermission) => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { IPType } from "@app/lib/ip";
|
||||
import { TOrgPermission } from "@app/lib/types";
|
||||
|
||||
export type TCreateIdentityDTO = {
|
||||
@@ -14,3 +15,9 @@ export type TUpdateIdentityDTO = {
|
||||
export type TDeleteIdentityDTO = {
|
||||
id: string;
|
||||
} & Omit<TOrgPermission, "orgId">;
|
||||
|
||||
export interface TIdentityTrustedIp {
|
||||
ipAddress: string;
|
||||
type: IPType;
|
||||
prefix: number;
|
||||
}
|
||||
|
||||
@@ -998,7 +998,7 @@ export const integrationAuthServiceFactory = ({
|
||||
};
|
||||
|
||||
return {
|
||||
listIntegrationAuthByProjectId,
|
||||
listIntegrationAuthByProjectId,
|
||||
getIntegrationOptions,
|
||||
getIntegrationAuth,
|
||||
oauthExchange,
|
||||
|
||||
@@ -7,7 +7,7 @@ export type TGetIntegrationAuthDTO = {
|
||||
export type TOauthExchangeDTO = {
|
||||
integration: string;
|
||||
code: string;
|
||||
url: string;
|
||||
url?: string;
|
||||
} & TProjectPermission;
|
||||
|
||||
export type TSaveIntegrationAccessTokenDTO = {
|
||||
|
||||
@@ -91,7 +91,7 @@ export const integrationServiceFactory = ({
|
||||
});
|
||||
|
||||
// TODO(akhilmhdh-pg): audit log
|
||||
return integration;
|
||||
return { integration, integrationAuth };
|
||||
};
|
||||
|
||||
const updateIntegration = async ({
|
||||
@@ -150,7 +150,7 @@ export const integrationServiceFactory = ({
|
||||
);
|
||||
|
||||
const deletedIntegration = await integrationDal.deleteById(id);
|
||||
return deletedIntegration;
|
||||
return { ...integration, ...deletedIntegration };
|
||||
};
|
||||
|
||||
const listIntegrationByProject = async ({ actor, actorId, projectId }: TProjectPermission) => {
|
||||
|
||||
10
backend-pg/src/services/org/org-bot-dal.ts
Normal file
10
backend-pg/src/services/org/org-bot-dal.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { TDbClient } from "@app/db";
|
||||
import { TableName } from "@app/db/schemas";
|
||||
import { ormify } from "@app/lib/knex";
|
||||
|
||||
export type TOrgBotDalFactory = ReturnType<typeof orgBotDalFactory>;
|
||||
|
||||
export const orgBotDalFactory = (db: TDbClient) => {
|
||||
const orgBotOrm = ormify(db, TableName.OrgBot);
|
||||
return orgBotOrm;
|
||||
};
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
TOrgMembershipsUpdate
|
||||
} from "@app/db/schemas";
|
||||
import { DatabaseError } from "@app/lib/errors";
|
||||
import { withTransaction } from "@app/lib/knex";
|
||||
import { ormify, withTransaction } from "@app/lib/knex";
|
||||
|
||||
export type TOrgDalFactory = ReturnType<typeof orgDalFactory>;
|
||||
|
||||
@@ -110,14 +110,7 @@ export const orgDalFactory = (db: TDbClient) => {
|
||||
|
||||
// MEMBERSHIP OPERATIONS
|
||||
// --------------------
|
||||
const findMembership = async (filter: Partial<TOrgMemberships>, tx?: Knex) => {
|
||||
try {
|
||||
const membership = await (tx || db)(TableName.OrgMembership).where(filter);
|
||||
return membership;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Find org membership" });
|
||||
}
|
||||
};
|
||||
const orgMembershipOrm = ormify(db, TableName.OrgMembership);
|
||||
|
||||
const createMembership = async (data: TOrgMembershipsInsert, tx?: Knex) => {
|
||||
try {
|
||||
@@ -175,7 +168,7 @@ export const orgDalFactory = (db: TDbClient) => {
|
||||
create,
|
||||
updateById,
|
||||
deleteById,
|
||||
findMembership,
|
||||
findMembership: orgMembershipOrm.find,
|
||||
createMembership,
|
||||
updateMembershipById,
|
||||
deleteMembershipById,
|
||||
|
||||
@@ -8,6 +8,8 @@ import {
|
||||
} from "@app/ee/services/permission/org-permission";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { generateAsymmetricKeyPair } from "@app/lib/crypto";
|
||||
import { generateSymmetricKey, infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
|
||||
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||
import { isDisposableEmail } from "@app/lib/validator";
|
||||
|
||||
@@ -17,6 +19,7 @@ import { TokenType } from "../auth-token/auth-token-types";
|
||||
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
|
||||
import { TUserDalFactory } from "../user/user-dal";
|
||||
import { TIncidentContactsDalFactory } from "./incident-contacts-dal";
|
||||
import { TOrgBotDalFactory } from "./org-bot-dal";
|
||||
import { TOrgDalFactory } from "./org-dal";
|
||||
import { TOrgRoleDalFactory } from "./org-role-dal";
|
||||
import {
|
||||
@@ -28,6 +31,7 @@ import {
|
||||
|
||||
type TOrgServiceFactoryDep = {
|
||||
orgDal: TOrgDalFactory;
|
||||
orgBotDal: TOrgBotDalFactory;
|
||||
orgRoleDal: TOrgRoleDalFactory;
|
||||
userDal: TUserDalFactory;
|
||||
incidentContactDal: TIncidentContactsDalFactory;
|
||||
@@ -45,7 +49,8 @@ export const orgServiceFactory = ({
|
||||
incidentContactDal,
|
||||
permissionService,
|
||||
smtpService,
|
||||
tokenService
|
||||
tokenService,
|
||||
orgBotDal
|
||||
}: TOrgServiceFactoryDep) => {
|
||||
/*
|
||||
* Get organization details by the organization id
|
||||
@@ -95,6 +100,23 @@ export const orgServiceFactory = ({
|
||||
* Create organization
|
||||
* */
|
||||
const createOrganization = async (userId: string, orgName: string) => {
|
||||
const { privateKey, publicKey } = generateAsymmetricKeyPair();
|
||||
const key = generateSymmetricKey();
|
||||
const {
|
||||
ciphertext: encryptedPrivateKey,
|
||||
iv: privateKeyIV,
|
||||
tag: privateKeyTag,
|
||||
encoding: privateKeyKeyEncoding,
|
||||
algorithm: privateKeyAlgorithm
|
||||
} = infisicalSymmetricEncypt(privateKey);
|
||||
const {
|
||||
ciphertext: encryptedSymmetricKey,
|
||||
iv: symmetricKeyIV,
|
||||
tag: symmetricKeyTag,
|
||||
encoding: symmetricKeyKeyEncoding,
|
||||
algorithm: symmetricKeyAlgorithm
|
||||
} = infisicalSymmetricEncypt(key);
|
||||
|
||||
const organization = await orgDal.transaction(async (tx) => {
|
||||
const org = await orgDal.create({ name: orgName }, tx);
|
||||
await orgDal.createMembership(
|
||||
@@ -106,6 +128,24 @@ export const orgServiceFactory = ({
|
||||
},
|
||||
tx
|
||||
);
|
||||
await orgBotDal.create(
|
||||
{
|
||||
name: org.name,
|
||||
publicKey,
|
||||
privateKeyIV,
|
||||
encryptedPrivateKey,
|
||||
symmetricKeyIV,
|
||||
symmetricKeyTag,
|
||||
encryptedSymmetricKey,
|
||||
symmetricKeyAlgorithm,
|
||||
orgId: org.id,
|
||||
privateKeyTag,
|
||||
privateKeyAlgorithm,
|
||||
privateKeyKeyEncoding,
|
||||
symmetricKeyKeyEncoding
|
||||
},
|
||||
tx
|
||||
);
|
||||
return org;
|
||||
});
|
||||
|
||||
@@ -178,7 +218,7 @@ export const orgServiceFactory = ({
|
||||
// Thus the signup flow is not needed anymore
|
||||
const [inviteeMembership] = await orgDal.findMembership(
|
||||
{ orgId, userId: inviteeUser.id },
|
||||
tx
|
||||
{ tx }
|
||||
);
|
||||
if (inviteeMembership && inviteeMembership.status === OrgMembershipStatus.Accepted) {
|
||||
throw new BadRequestError({
|
||||
|
||||
@@ -58,6 +58,9 @@ export const projectEnvServiceFactory = ({
|
||||
ProjectPermissionSub.Environments
|
||||
);
|
||||
|
||||
const oldEnv = await projectEnvDal.findOne({ id, projectId });
|
||||
if (!oldEnv) throw new BadRequestError({ message: "Environment not found" });
|
||||
|
||||
if (slug) {
|
||||
const existingEnv = await projectEnvDal.findOne({ slug });
|
||||
if (existingEnv && existingEnv.id !== id) {
|
||||
@@ -68,8 +71,8 @@ export const projectEnvServiceFactory = ({
|
||||
}
|
||||
}
|
||||
|
||||
const [env] = await projectEnvDal.update({ id, projectId }, { name, slug });
|
||||
return env;
|
||||
const env = await projectEnvDal.updateById(oldEnv.id, { name, slug });
|
||||
return { environment: env, old: oldEnv };
|
||||
};
|
||||
|
||||
const deleteEnvironment = async ({ projectId, actor, actorId, id }: TDeleteEnvDTO) => {
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
} from "@app/ee/services/permission/project-permission";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { groupBy } from "@app/lib/fn";
|
||||
|
||||
import { TOrgDalFactory } from "../org/org-dal";
|
||||
import { TProjectDalFactory } from "../project/project-dal";
|
||||
@@ -17,6 +18,7 @@ import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
|
||||
import { TUserDalFactory } from "../user/user-dal";
|
||||
import { TProjectMembershipDalFactory } from "./project-membership-dal";
|
||||
import {
|
||||
TAddUsersToWorkspaceDTO,
|
||||
TDeleteProjectMembershipDTO,
|
||||
TGetProjectMembershipDTO,
|
||||
TInviteUserToProjectDTO,
|
||||
@@ -31,7 +33,7 @@ type TProjectMembershipServiceFactoryDep = {
|
||||
projectRoleDal: Pick<TProjectRoleDalFactory, "findOne">;
|
||||
orgDal: Pick<TOrgDalFactory, "findMembership">;
|
||||
projectDal: Pick<TProjectDalFactory, "findById">;
|
||||
projectKeyDal: Pick<TProjectKeyDalFactory, "findLatestProjectKey" | "delete">;
|
||||
projectKeyDal: Pick<TProjectKeyDalFactory, "findLatestProjectKey" | "delete" | "insertMany">;
|
||||
};
|
||||
|
||||
export type TProjectMembershipServiceFactory = ReturnType<typeof projectMembershipServiceFactory>;
|
||||
@@ -122,6 +124,71 @@ export const projectMembershipServiceFactory = ({
|
||||
return { invitee, latestKey };
|
||||
};
|
||||
|
||||
const addUsersToProject = async ({
|
||||
projectId,
|
||||
actorId,
|
||||
actor,
|
||||
members
|
||||
}: TAddUsersToWorkspaceDTO) => {
|
||||
const project = await projectDal.findById(projectId);
|
||||
if (!project) throw new BadRequestError({ message: "Project not found" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.Member
|
||||
);
|
||||
const orgMembers = await orgDal.findMembership({
|
||||
orgId: project.orgId,
|
||||
$in: { id: members.map(({ orgMembershipId }) => orgMembershipId) }
|
||||
});
|
||||
if (orgMembers.length !== members.length)
|
||||
throw new BadRequestError({ message: "Some users are not part of org" });
|
||||
|
||||
const existingMembers = await projectMembershipDal.find({
|
||||
projectId,
|
||||
$in: { userId: orgMembers.map(({ userId }) => userId).filter(Boolean) as string[] }
|
||||
});
|
||||
if (existingMembers.length)
|
||||
throw new BadRequestError({ message: "Some users are already part of project" });
|
||||
|
||||
await projectMembershipDal.transaction(async (tx) => {
|
||||
await projectMembershipDal.insertMany(
|
||||
orgMembers.map(({ userId }) => ({
|
||||
projectId,
|
||||
userId: userId as string,
|
||||
role: ProjectMembershipRole.Member
|
||||
})),
|
||||
tx
|
||||
);
|
||||
const encKeyGroupByOrgMembId = groupBy(members, (i) => i.orgMembershipId);
|
||||
await projectKeyDal.insertMany(
|
||||
orgMembers.map(({ userId, id }) => ({
|
||||
encryptedKey: encKeyGroupByOrgMembId[id][0].workspaceEncryptedKey,
|
||||
nonce: encKeyGroupByOrgMembId[id][0].workspaceEncryptedNonce,
|
||||
senderId: actorId,
|
||||
receiverId: userId as string,
|
||||
projectId
|
||||
})),
|
||||
tx
|
||||
);
|
||||
});
|
||||
const sender = await userDal.findById(actorId);
|
||||
const appCfg = getConfig();
|
||||
await smtpService.sendMail({
|
||||
template: SmtpTemplates.WorkspaceInvite,
|
||||
subjectLine: "Infisical workspace invitation",
|
||||
recipients: orgMembers.map(({ userId }) => userId).filter(Boolean) as string[],
|
||||
substitutions: {
|
||||
inviterFirstName: sender.firstName,
|
||||
inviterEmail: sender.email,
|
||||
workspaceName: project.name,
|
||||
callback_url: `${appCfg.SITE_URL}/login`
|
||||
}
|
||||
});
|
||||
return orgMembers;
|
||||
};
|
||||
|
||||
const updateProjectMembership = async ({
|
||||
actorId,
|
||||
actor,
|
||||
@@ -186,6 +253,7 @@ export const projectMembershipServiceFactory = ({
|
||||
getProjectMemberships,
|
||||
inviteUserToProject,
|
||||
updateProjectMembership,
|
||||
deleteProjectMembership
|
||||
deleteProjectMembership,
|
||||
addUsersToProject
|
||||
};
|
||||
};
|
||||
|
||||
@@ -14,3 +14,11 @@ export type TUpdateProjectMembershipDTO = {
|
||||
export type TDeleteProjectMembershipDTO = {
|
||||
membershipId: string;
|
||||
} & TProjectPermission;
|
||||
|
||||
export type TAddUsersToWorkspaceDTO = {
|
||||
members: {
|
||||
orgMembershipId: string;
|
||||
workspaceEncryptedKey: string;
|
||||
workspaceEncryptedNonce: string;
|
||||
}[];
|
||||
} & TProjectPermission;
|
||||
|
||||
@@ -91,14 +91,16 @@ export const secretFolderServiceFactory = ({
|
||||
subject(ProjectPermissionSub.Secrets, { environment, secretPath: path })
|
||||
);
|
||||
|
||||
const parentFolder = await folderDal.findBySecretPath(projectId, environment, path);
|
||||
if (!parentFolder) throw new BadRequestError({ message: "Secret path not found" });
|
||||
|
||||
const env = await projectEnvDal.findOne({ projectId, slug: environment });
|
||||
if (!env)
|
||||
throw new BadRequestError({ message: "Environment not found", name: "Create folder" });
|
||||
|
||||
const folder = await folderDal.transaction(async (tx) => {
|
||||
const parentFolder = await folderDal.findBySecretPath(projectId, environment, path, tx);
|
||||
if (!parentFolder) throw new BadRequestError({ message: "Secret path not found" });
|
||||
throw new BadRequestError({ message: "Environment not found", name: "Update folder" });
|
||||
const folder = await folderDal.findOne({ envId: env.id, id, parentId: parentFolder.id });
|
||||
if (!folder) throw new BadRequestError({ message: "Folder not found" });
|
||||
|
||||
const newFolder = await folderDal.transaction(async (tx) => {
|
||||
const [doc] = await folderDal.update(
|
||||
{ envId: env.id, id, parentId: parentFolder.id },
|
||||
{ name },
|
||||
@@ -117,8 +119,8 @@ export const secretFolderServiceFactory = ({
|
||||
return doc;
|
||||
});
|
||||
|
||||
await snapshotService.performSnapshot(folder.parentId as string);
|
||||
return folder;
|
||||
await snapshotService.performSnapshot(newFolder.parentId as string);
|
||||
return { folder: newFolder, old: folder };
|
||||
};
|
||||
|
||||
const deleteFolder = async ({
|
||||
|
||||
@@ -6,8 +6,10 @@ import {
|
||||
ProjectPermissionSub
|
||||
} from "@app/ee/services/permission/project-permission";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { groupBy } from "@app/lib/fn";
|
||||
|
||||
import { TProjectEnvDalFactory } from "../project-env/project-env-dal";
|
||||
import { TSecretDalFactory } from "../secret/secret-dal";
|
||||
import { TSecretFolderDalFactory } from "../secret-folder/secret-folder-dal";
|
||||
import { TSecretImportDalFactory } from "./secret-import-dal";
|
||||
import {
|
||||
@@ -17,8 +19,6 @@ import {
|
||||
TGetSecretsFromImportDTO,
|
||||
TUpdateSecretImportDTO
|
||||
} from "./secret-import-types";
|
||||
import { TSecretDalFactory } from "../secret/secret-dal";
|
||||
import { groupBy } from "@app/lib/fn";
|
||||
|
||||
type TSecretImportServiceFactoryDep = {
|
||||
secretImportDal: TSecretImportDalFactory;
|
||||
|
||||
@@ -256,9 +256,10 @@ export const secretServiceFactory = ({
|
||||
}
|
||||
|
||||
const { keyName2BlindIndex } = await fnSecretBlindIndexCheck({
|
||||
inputSecrets: [{ secretName: inputSecret.secretName }],
|
||||
inputSecrets: [{ secretName: inputSecret.secretName, type: inputSecret.type as SecretType }],
|
||||
folderId,
|
||||
isNew: true,
|
||||
userId: actorId,
|
||||
blindIndexCfg
|
||||
});
|
||||
|
||||
@@ -539,6 +540,7 @@ export const secretServiceFactory = ({
|
||||
fnSecretBulkInsert({
|
||||
inputSecrets: inputSecrets.map(({ secretName, ...el }) => ({
|
||||
...el,
|
||||
version:0,
|
||||
secretBlindIndex: keyName2BlindIndex[secretName],
|
||||
type: SecretType.Shared,
|
||||
algorithm: SecretEncryptionAlgo.AES_256_GCM,
|
||||
|
||||
@@ -18,7 +18,7 @@ export const useCreateProjectRole = () => {
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ projectId, permissions, ...dto }: TCreateProjectRoleDTO) =>
|
||||
apiRequest.post(`/api/ee/v1/workspace/${projectId}/roles`, {
|
||||
apiRequest.post(`/api/v1/workspace/${projectId}/roles`, {
|
||||
...dto,
|
||||
permissions: permissions.length ? packRules(permissions) : []
|
||||
}),
|
||||
@@ -33,7 +33,7 @@ export const useUpdateProjectRole = () => {
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id, projectId, permissions, ...dto }: TUpdateProjectRoleDTO) =>
|
||||
apiRequest.patch(`/api/ee/v1/workspace/${projectId}/roles/${id}`, {
|
||||
apiRequest.patch(`/api/v1/workspace/${projectId}/roles/${id}`, {
|
||||
...dto,
|
||||
permissions: permissions?.length ? packRules(permissions) : []
|
||||
}),
|
||||
@@ -48,7 +48,7 @@ export const useDeleteProjectRole = () => {
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ projectId, id }: TDeleteProjectRoleDTO) =>
|
||||
apiRequest.delete(`/api/ee/v1/workspace/${projectId}/roles/${id}`, {
|
||||
apiRequest.delete(`/api/v1/workspace/${projectId}/roles/${id}`, {
|
||||
data: { projectId }
|
||||
}),
|
||||
onSuccess: (_, { projectId }) => {
|
||||
@@ -62,7 +62,7 @@ export const useCreateOrgRole = () => {
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ orgId, permissions, ...dto }: TCreateOrgRoleDTO) =>
|
||||
apiRequest.post(`/api/ee/v1/organization/${orgId}/roles`, {
|
||||
apiRequest.post(`/api/v1/organization/${orgId}/roles`, {
|
||||
...dto,
|
||||
permissions: permissions.length ? packRules(permissions) : []
|
||||
}),
|
||||
@@ -77,7 +77,7 @@ export const useUpdateOrgRole = () => {
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id, orgId, permissions, ...dto }: TUpdateOrgRoleDTO) =>
|
||||
apiRequest.patch(`/api/ee/v1/organization/${orgId}/roles/${id}`, {
|
||||
apiRequest.patch(`/api/v1/organization/${orgId}/roles/${id}`, {
|
||||
...dto,
|
||||
permissions: permissions?.length ? packRules(permissions) : []
|
||||
}),
|
||||
@@ -92,7 +92,7 @@ export const useDeleteOrgRole = () => {
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ orgId, id }: TDeleteOrgRoleDTO) =>
|
||||
apiRequest.delete(`/api/ee/v1/organization/${orgId}/roles/${id}`, {
|
||||
apiRequest.delete(`/api/v1/organization/${orgId}/roles/${id}`, {
|
||||
data: { orgId }
|
||||
}),
|
||||
onSuccess: (_, { orgId }) => {
|
||||
|
||||
@@ -48,7 +48,7 @@ export const roleQueryKeys = {
|
||||
const getProjectRoles = async (projectId: string) => {
|
||||
const { data } = await apiRequest.get<{
|
||||
data: { roles: Array<Omit<TProjectRole, "permissions"> & { permissions: unknown }> };
|
||||
}>(`/api/ee/v1/workspace/${projectId}/roles`);
|
||||
}>(`/api/v1/workspace/${projectId}/roles`);
|
||||
return data.data.roles.map(({ permissions, ...el }) => ({
|
||||
...el,
|
||||
permissions: unpackRules(permissions as PackRule<TProjectPermission>[])
|
||||
@@ -65,7 +65,7 @@ export const useGetProjectRoles = (projectId: string) =>
|
||||
const getOrgRoles = async (orgId: string) => {
|
||||
const { data } = await apiRequest.get<{
|
||||
data: { roles: Array<Omit<TOrgRole, "permissions"> & { permissions: unknown }> };
|
||||
}>(`/api/ee/v1/organization/${orgId}/roles`);
|
||||
}>(`/api/v1/organization/${orgId}/roles`);
|
||||
return data.data.roles.map(({ permissions, ...el }) => ({
|
||||
...el,
|
||||
permissions: unpackRules(permissions as PackRule<TPermission>[])
|
||||
@@ -85,7 +85,7 @@ const getUserOrgPermissions = async ({ orgId }: TGetUserOrgPermissionsDTO) => {
|
||||
const { data } = await apiRequest.get<{
|
||||
permissions: PackRule<RawRuleOf<MongoAbility<OrgPermissionSet>>>[];
|
||||
membership: OrgUser;
|
||||
}>(`/api/ee/v1/organization/${orgId}/permissions`);
|
||||
}>(`/api/v1/organization/${orgId}/permissions`);
|
||||
|
||||
return data;
|
||||
};
|
||||
@@ -105,7 +105,7 @@ export const useGetUserOrgPermissions = ({ orgId }: TGetUserOrgPermissionsDTO) =
|
||||
const getUserProjectPermissions = async ({ workspaceId }: TGetUserProjectPermissionDTO) => {
|
||||
const { data } = await apiRequest.get<{
|
||||
data: { permissions: PackRule<RawRuleOf<MongoAbility<OrgPermissionSet>>>[] };
|
||||
}>(`/api/ee/v1/workspace/${workspaceId}/permissions`, {});
|
||||
}>(`/api/v1/workspace/${workspaceId}/permissions`, {});
|
||||
|
||||
return data.data.permissions;
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@ export const useCreateSecretApprovalPolicy = () => {
|
||||
|
||||
return useMutation<{}, {}, TCreateSecretPolicyDTO>({
|
||||
mutationFn: async ({ environment, workspaceId, approvals, approvers, secretPath, name }) => {
|
||||
const { data } = await apiRequest.post("/api/ee/v1/secret-approvals", {
|
||||
const { data } = await apiRequest.post("/api/v1/secret-approvals", {
|
||||
environment,
|
||||
workspaceId,
|
||||
approvals,
|
||||
@@ -31,7 +31,7 @@ export const useUpdateSecretApprovalPolicy = () => {
|
||||
|
||||
return useMutation<{}, {}, TUpdateSecretPolicyDTO>({
|
||||
mutationFn: async ({ id, approvers, approvals, secretPath, name }) => {
|
||||
const { data } = await apiRequest.patch(`/api/ee/v1/secret-approvals/${id}`, {
|
||||
const { data } = await apiRequest.patch(`/api/v1/secret-approvals/${id}`, {
|
||||
approvals,
|
||||
approvers,
|
||||
secretPath,
|
||||
@@ -50,7 +50,7 @@ export const useDeleteSecretApprovalPolicy = () => {
|
||||
|
||||
return useMutation<{}, {}, TDeleteSecretPolicyDTO>({
|
||||
mutationFn: async ({ id }) => {
|
||||
const { data } = await apiRequest.delete(`/api/ee/v1/secret-approvals/${id}`);
|
||||
const { data } = await apiRequest.delete(`/api/v1/secret-approvals/${id}`);
|
||||
return data;
|
||||
},
|
||||
onSuccess: (_, { workspaceId }) => {
|
||||
|
||||
@@ -19,7 +19,7 @@ export const secretApprovalKeys = {
|
||||
|
||||
const fetchApprovalPolicies = async (workspaceId: string) => {
|
||||
const { data } = await apiRequest.get<{ approvals: TSecretApprovalPolicy[] }>(
|
||||
"/api/ee/v1/secret-approvals",
|
||||
"/api/v1/secret-approvals",
|
||||
{ params: { workspaceId } }
|
||||
);
|
||||
return data.approvals;
|
||||
@@ -49,7 +49,7 @@ const fetchApprovalPolicyOfABoard = async (
|
||||
secretPath: string
|
||||
) => {
|
||||
const { data } = await apiRequest.get<{ policy: TSecretApprovalPolicy }>(
|
||||
"/api/ee/v1/secret-approvals/board",
|
||||
"/api/v1/secret-approvals/board",
|
||||
{ params: { workspaceId, environment, secretPath } }
|
||||
);
|
||||
return data.policy || "";
|
||||
|
||||
@@ -14,7 +14,7 @@ export const useUpdateSecretApprovalReviewStatus = () => {
|
||||
|
||||
return useMutation<{}, {}, TUpdateSecretApprovalReviewStatusDTO>({
|
||||
mutationFn: async ({ id, status }) => {
|
||||
const { data } = await apiRequest.post(`/api/ee/v1/secret-approval-requests/${id}/review`, {
|
||||
const { data } = await apiRequest.post(`/api/v1/secret-approval-requests/${id}/review`, {
|
||||
status
|
||||
});
|
||||
return data;
|
||||
@@ -30,7 +30,7 @@ export const useUpdateSecretApprovalRequestStatus = () => {
|
||||
|
||||
return useMutation<{}, {}, TUpdateSecretApprovalRequestStatusDTO>({
|
||||
mutationFn: async ({ id, status }) => {
|
||||
const { data } = await apiRequest.post(`/api/ee/v1/secret-approval-requests/${id}/status`, {
|
||||
const { data } = await apiRequest.post(`/api/v1/secret-approval-requests/${id}/status`, {
|
||||
status
|
||||
});
|
||||
return data;
|
||||
@@ -47,7 +47,7 @@ export const usePerformSecretApprovalRequestMerge = () => {
|
||||
|
||||
return useMutation<{}, {}, TPerformSecretApprovalRequestMerge>({
|
||||
mutationFn: async ({ id }) => {
|
||||
const { data } = await apiRequest.post(`/api/ee/v1/secret-approval-requests/${id}/merge`);
|
||||
const { data } = await apiRequest.post(`/api/v1/secret-approval-requests/${id}/merge`);
|
||||
return data;
|
||||
},
|
||||
onSuccess: (_, { id, workspaceId }) => {
|
||||
|
||||
@@ -96,7 +96,7 @@ const fetchSecretApprovalRequestList = async ({
|
||||
offset
|
||||
}: TGetSecretApprovalRequestList) => {
|
||||
const { data } = await apiRequest.get<{ approvals: TSecretApprovalRequest[] }>(
|
||||
"/api/ee/v1/secret-approval-requests",
|
||||
"/api/v1/secret-approval-requests",
|
||||
{
|
||||
params: {
|
||||
workspaceId,
|
||||
@@ -158,7 +158,7 @@ const fetchSecretApprovalRequestDetails = async ({
|
||||
id
|
||||
}: Omit<TGetSecretApprovalRequestDetails, "decryptKey">) => {
|
||||
const { data } = await apiRequest.get<{ approval: TSecretApprovalRequest }>(
|
||||
`/api/ee/v1/secret-approval-requests/${id}`
|
||||
`/api/v1/secret-approval-requests/${id}`
|
||||
);
|
||||
|
||||
return data.approval;
|
||||
@@ -197,7 +197,7 @@ export const useGetSecretApprovalRequestDetails = ({
|
||||
|
||||
const fetchSecretApprovalRequestCount = async ({ workspaceId }: TGetSecretApprovalRequestCount) => {
|
||||
const { data } = await apiRequest.get<{ approvals: TSecretApprovalRequestCount }>(
|
||||
"/api/ee/v1/secret-approval-requests/count",
|
||||
"/api/v1/secret-approval-requests/count",
|
||||
{ params: { workspaceId } }
|
||||
);
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ export const useCreateSecretRotation = () => {
|
||||
|
||||
return useMutation<{}, {}, TCreateSecretRotationDTO>({
|
||||
mutationFn: async (dto) => {
|
||||
const { data } = await apiRequest.post("/api/ee/v1/secret-rotations", dto);
|
||||
const { data } = await apiRequest.post("/api/v1/secret-rotations", dto);
|
||||
return data;
|
||||
},
|
||||
onSuccess: (_, { workspaceId }) => {
|
||||
@@ -28,7 +28,7 @@ export const useDeleteSecretRotation = () => {
|
||||
|
||||
return useMutation<{}, {}, TDeleteSecretRotationDTO>({
|
||||
mutationFn: async (dto) => {
|
||||
const { data } = await apiRequest.delete(`/api/ee/v1/secret-rotations/${dto.id}`);
|
||||
const { data } = await apiRequest.delete(`/api/v1/secret-rotations/${dto.id}`);
|
||||
return data;
|
||||
},
|
||||
onSuccess: (_, { workspaceId }) => {
|
||||
@@ -42,7 +42,7 @@ export const useRestartSecretRotation = () => {
|
||||
|
||||
return useMutation<{}, {}, TRestartSecretRotationDTO>({
|
||||
mutationFn: async (dto) => {
|
||||
const { data } = await apiRequest.post("/api/ee/v1/secret-rotations/restart", { id: dto.id });
|
||||
const { data } = await apiRequest.post("/api/v1/secret-rotations/restart", { id: dto.id });
|
||||
return data;
|
||||
},
|
||||
onSuccess: (_, { workspaceId }) => {
|
||||
|
||||
@@ -25,7 +25,7 @@ export const secretRotationKeys = {
|
||||
|
||||
const fetchSecretRotationProviders = async ({ workspaceId }: TGetSecretRotationProviders) => {
|
||||
const { data } = await apiRequest.get<TSecretRotationProviderList>(
|
||||
`/api/ee/v1/secret-rotation-providers/${workspaceId}`
|
||||
`/api/v1/secret-rotation-providers/${workspaceId}`
|
||||
);
|
||||
return data;
|
||||
};
|
||||
@@ -55,7 +55,7 @@ const fetchSecretRotations = async ({
|
||||
workspaceId
|
||||
}: Omit<TGetSecretRotationList, "decryptFileKey">) => {
|
||||
const { data } = await apiRequest.get<{ secretRotations: TSecretRotation[] }>(
|
||||
"/api/ee/v1/secret-rotations",
|
||||
"/api/v1/secret-rotations",
|
||||
{ params: { workspaceId } }
|
||||
);
|
||||
return data.secretRotations;
|
||||
|
||||
@@ -35,7 +35,7 @@ const fetchWorkspaceSnaphots = async ({
|
||||
offset = 0
|
||||
}: TGetSecretSnapshotsDTO & { offset: number }) => {
|
||||
const res = await apiRequest.get<{ secretSnapshots: TSecretSnapshot[] }>(
|
||||
`/api/ee/v1/workspace/${workspaceId}/secret-snapshots`,
|
||||
`/api/v1/workspace/${workspaceId}/secret-snapshots`,
|
||||
{
|
||||
params: {
|
||||
limit,
|
||||
@@ -60,7 +60,7 @@ export const useGetWorkspaceSnapshotList = (dto: TGetSecretSnapshotsDTO & { isPa
|
||||
|
||||
const fetchSnapshotEncSecrets = async (snapshotId: string) => {
|
||||
const res = await apiRequest.get<{ secretSnapshot: TSnapshotData }>(
|
||||
`/api/ee/v1/secret-snapshot/${snapshotId}`
|
||||
`/api/v1/secret-snapshot/${snapshotId}`
|
||||
);
|
||||
return res.data.secretSnapshot;
|
||||
};
|
||||
@@ -142,7 +142,7 @@ const fetchWorkspaceSecretSnaphotCount = async (
|
||||
directory = "/"
|
||||
) => {
|
||||
const res = await apiRequest.get<{ count: number }>(
|
||||
`/api/ee/v1/workspace/${workspaceId}/secret-snapshots/count`,
|
||||
`/api/v1/workspace/${workspaceId}/secret-snapshots/count`,
|
||||
{
|
||||
params: {
|
||||
environment,
|
||||
@@ -170,7 +170,7 @@ export const usePerformSecretRollback = () => {
|
||||
|
||||
return useMutation<{}, {}, TSecretRollbackDTO>({
|
||||
mutationFn: async ({ snapshotId }) => {
|
||||
const { data } = await apiRequest.post(`/api/ee/v1/secret-snapshot/${snapshotId}/rollback`);
|
||||
const { data } = await apiRequest.post(`/api/v1/secret-snapshot/${snapshotId}/rollback`);
|
||||
return data;
|
||||
},
|
||||
onSuccess: (_, { workspaceId, environment, directory }) => {
|
||||
|
||||
@@ -3,22 +3,20 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
const ssoConfigKeys = {
|
||||
getSSOConfig: (orgId: string) => [{ orgId }, "organization-saml-sso"] as const,
|
||||
}
|
||||
getSSOConfig: (orgId: string) => [{ orgId }, "organization-saml-sso"] as const
|
||||
};
|
||||
|
||||
export const useGetSSOConfig = (organizationId: string) => {
|
||||
return useQuery({
|
||||
queryKey: ssoConfigKeys.getSSOConfig(organizationId),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get(
|
||||
`/api/v1/sso/config?organizationId=${organizationId}`
|
||||
);
|
||||
const { data } = await apiRequest.get(`/api/v1/sso/config?organizationId=${organizationId}`);
|
||||
|
||||
return data;
|
||||
},
|
||||
enabled: true
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const useCreateSSOConfig = () => {
|
||||
const queryClient = useQueryClient();
|
||||
@@ -38,18 +36,15 @@ export const useCreateSSOConfig = () => {
|
||||
issuer: string;
|
||||
cert: string;
|
||||
}) => {
|
||||
const { data } = await apiRequest.post(
|
||||
"/api/v1/sso/config",
|
||||
{
|
||||
organizationId,
|
||||
authProvider,
|
||||
isActive,
|
||||
entryPoint,
|
||||
issuer,
|
||||
cert
|
||||
}
|
||||
);
|
||||
|
||||
const { data } = await apiRequest.post("/api/v1/sso/config", {
|
||||
organizationId,
|
||||
authProvider,
|
||||
isActive,
|
||||
entryPoint,
|
||||
issuer,
|
||||
cert
|
||||
});
|
||||
|
||||
return data;
|
||||
},
|
||||
onSuccess(_, dto) {
|
||||
@@ -76,22 +71,19 @@ export const useUpdateSSOConfig = () => {
|
||||
issuer?: string;
|
||||
cert?: string;
|
||||
}) => {
|
||||
const { data } = await apiRequest.patch(
|
||||
"/api/v1/sso/config",
|
||||
{
|
||||
organizationId,
|
||||
...(authProvider !== undefined ? { authProvider } : {}),
|
||||
...(isActive !== undefined ? { isActive } : {}),
|
||||
...(entryPoint !== undefined ? { entryPoint } : {}),
|
||||
...(issuer !== undefined ? { issuer } : {}),
|
||||
...(cert !== undefined ? { cert } : {})
|
||||
}
|
||||
);
|
||||
|
||||
const { data } = await apiRequest.patch("/api/v1/sso/config", {
|
||||
organizationId,
|
||||
...(authProvider !== undefined ? { authProvider } : {}),
|
||||
...(isActive !== undefined ? { isActive } : {}),
|
||||
...(entryPoint !== undefined ? { entryPoint } : {}),
|
||||
...(issuer !== undefined ? { issuer } : {}),
|
||||
...(cert !== undefined ? { cert } : {})
|
||||
});
|
||||
|
||||
return data;
|
||||
},
|
||||
onSuccess(_, dto) {
|
||||
queryClient.invalidateQueries(ssoConfigKeys.getSSOConfig(dto.organizationId));
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user