feat(infisical-pg): completed audit log moved ee routes to normal routing pattern

This commit is contained in:
Akhil Mohan
2024-01-27 12:38:23 +05:30
parent 6e6a1c87f2
commit 16f0ac6d43
78 changed files with 3178 additions and 445 deletions
-89
View File
@@ -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();
+223 -18
View File
@@ -18,6 +18,7 @@
"@fastify/session": "^10.7.0", "@fastify/session": "^10.7.0",
"@fastify/swagger": "^8.12.0", "@fastify/swagger": "^8.12.0",
"@fastify/swagger-ui": "^1.10.1", "@fastify/swagger-ui": "^1.10.1",
"@node-saml/passport-saml": "^4.0.4",
"@octokit/rest": "^20.0.2", "@octokit/rest": "^20.0.2",
"@ucast/mongo2js": "^1.3.4", "@ucast/mongo2js": "^1.3.4",
"ajv": "^8.12.0", "ajv": "^8.12.0",
@@ -996,6 +997,64 @@
"win32" "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": { "node_modules/@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -1640,7 +1699,6 @@
"version": "1.19.5", "version": "1.19.5",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
"integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==",
"dev": true,
"dependencies": { "dependencies": {
"@types/connect": "*", "@types/connect": "*",
"@types/node": "*" "@types/node": "*"
@@ -1650,16 +1708,22 @@
"version": "3.4.38", "version": "3.4.38",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
"integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
"dev": true,
"dependencies": { "dependencies": {
"@types/node": "*" "@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": { "node_modules/@types/express": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
"integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
"dev": true,
"dependencies": { "dependencies": {
"@types/body-parser": "*", "@types/body-parser": "*",
"@types/express-serve-static-core": "^4.17.33", "@types/express-serve-static-core": "^4.17.33",
@@ -1671,7 +1735,6 @@
"version": "4.17.41", "version": "4.17.41",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz",
"integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==", "integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==",
"dev": true,
"dependencies": { "dependencies": {
"@types/node": "*", "@types/node": "*",
"@types/qs": "*", "@types/qs": "*",
@@ -1682,8 +1745,7 @@
"node_modules/@types/http-errors": { "node_modules/@types/http-errors": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz",
"integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA=="
"dev": true
}, },
"node_modules/@types/jmespath": { "node_modules/@types/jmespath": {
"version": "0.15.2", "version": "0.15.2",
@@ -1719,14 +1781,17 @@
"node_modules/@types/mime": { "node_modules/@types/mime": {
"version": "1.3.5", "version": "1.3.5",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="
"dev": true },
"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": { "node_modules/@types/node": {
"version": "20.9.5", "version": "20.9.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.5.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.5.tgz",
"integrity": "sha512-Uq2xbNq0chGg+/WQEU0LJTSs/1nKxz6u1iemLcGomkSnKokbW1fbLqc3HOqCf2JP7KjlL4QkS7oZZTrOQHQYgQ==", "integrity": "sha512-Uq2xbNq0chGg+/WQEU0LJTSs/1nKxz6u1iemLcGomkSnKokbW1fbLqc3HOqCf2JP7KjlL4QkS7oZZTrOQHQYgQ==",
"dev": true,
"dependencies": { "dependencies": {
"undici-types": "~5.26.4" "undici-types": "~5.26.4"
} }
@@ -1753,7 +1818,6 @@
"version": "1.0.16", "version": "1.0.16",
"resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.16.tgz", "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.16.tgz",
"integrity": "sha512-FD0qD5hbPWQzaM0wHUnJ/T0BBCJBxCeemtnCwc/ThhTg3x9jfrAcRUmj5Dopza+MfFS9acTe3wk7rcVnRIp/0A==", "integrity": "sha512-FD0qD5hbPWQzaM0wHUnJ/T0BBCJBxCeemtnCwc/ThhTg3x9jfrAcRUmj5Dopza+MfFS9acTe3wk7rcVnRIp/0A==",
"dev": true,
"dependencies": { "dependencies": {
"@types/express": "*" "@types/express": "*"
} }
@@ -1791,6 +1855,15 @@
"@types/passport": "*" "@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": { "node_modules/@types/pg": {
"version": "8.10.9", "version": "8.10.9",
"resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.10.9.tgz", "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.10.9.tgz",
@@ -1874,14 +1947,12 @@
"node_modules/@types/qs": { "node_modules/@types/qs": {
"version": "6.9.10", "version": "6.9.10",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz",
"integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==", "integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw=="
"dev": true
}, },
"node_modules/@types/range-parser": { "node_modules/@types/range-parser": {
"version": "1.2.7", "version": "1.2.7",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
"integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="
"dev": true
}, },
"node_modules/@types/semver": { "node_modules/@types/semver": {
"version": "7.5.6", "version": "7.5.6",
@@ -1892,7 +1963,6 @@
"version": "0.17.4", "version": "0.17.4",
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
"integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
"dev": true,
"dependencies": { "dependencies": {
"@types/mime": "^1", "@types/mime": "^1",
"@types/node": "*" "@types/node": "*"
@@ -1902,13 +1972,37 @@
"version": "1.15.5", "version": "1.15.5",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz",
"integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==",
"dev": true,
"dependencies": { "dependencies": {
"@types/http-errors": "*", "@types/http-errors": "*",
"@types/mime": "*", "@types/mime": "*",
"@types/node": "*" "@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": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "6.13.2", "version": "6.13.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.2.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.2.tgz",
@@ -2305,6 +2399,14 @@
"url": "https://opencollective.com/vitest" "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": { "node_modules/abbrev": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
@@ -6699,6 +6801,23 @@
"node": ">=6" "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": { "node_modules/passport-github": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/passport-github/-/passport-github-1.1.0.tgz", "resolved": "https://registry.npmjs.org/passport-github/-/passport-github-1.1.0.tgz",
@@ -6836,6 +6955,11 @@
"node": "*" "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": { "node_modules/pg": {
"version": "8.11.3", "version": "8.11.3",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", "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", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" "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": { "node_modules/secure-json-parse": {
"version": "2.7.0", "version": "2.7.0",
"resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz",
@@ -9295,8 +9424,7 @@
"node_modules/undici-types": { "node_modules/undici-types": {
"version": "5.26.5", "version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
"dev": true
}, },
"node_modules/universal-user-agent": { "node_modules/universal-user-agent": {
"version": "6.0.1", "version": "6.0.1",
@@ -10297,6 +10425,83 @@
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" "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": { "node_modules/xtend": {
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+1
View File
@@ -72,6 +72,7 @@
"@fastify/session": "^10.7.0", "@fastify/session": "^10.7.0",
"@fastify/swagger": "^8.12.0", "@fastify/swagger": "^8.12.0",
"@fastify/swagger-ui": "^1.10.1", "@fastify/swagger-ui": "^1.10.1",
"@node-saml/passport-saml": "^4.0.4",
"@octokit/rest": "^20.0.2", "@octokit/rest": "^20.0.2",
"@ucast/mongo2js": "^1.3.4", "@ucast/mongo2js": "^1.3.4",
"ajv": "^8.12.0", "ajv": "^8.12.0",
+7
View File
@@ -1,7 +1,10 @@
import "fastify"; import "fastify";
import { TUsers } from "@app/db/schemas"; 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 { 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 { 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 { TSecretApprovalRequestServiceFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-service";
import { TSecretRotationServiceFactory } from "@app/ee/services/secret-rotation/secret-rotation-service"; import { TSecretRotationServiceFactory } from "@app/ee/services/secret-rotation/secret-rotation-service";
@@ -62,6 +65,8 @@ declare module "fastify" {
isCompleted: string; isCompleted: string;
providerAuthToken: string; providerAuthToken: string;
}; };
auditLogInfo: Pick<TCreateAuditLogDTO, "userAgent" | "userAgentType" | "ipAddress" | "actor">;
ssoConfig: Awaited<ReturnType<TSamlConfigServiceFactory["getSaml"]>>;
} }
interface FastifyInstance { interface FastifyInstance {
@@ -98,6 +103,8 @@ declare module "fastify" {
secretApprovalRequest: TSecretApprovalRequestServiceFactory; secretApprovalRequest: TSecretApprovalRequestServiceFactory;
secretRotation: TSecretRotationServiceFactory; secretRotation: TSecretRotationServiceFactory;
snapshot: TSecretSnapshotServiceFactory; snapshot: TSecretSnapshotServiceFactory;
saml: TSamlConfigServiceFactory;
auditLog: TAuditLogServiceFactory;
}; };
// this is exclusive use for middlewares in which we need to inject data // this is exclusive use for middlewares in which we need to inject data
+16
View File
@@ -5,6 +5,9 @@ import {
TApiKeys, TApiKeys,
TApiKeysInsert, TApiKeysInsert,
TApiKeysUpdate, TApiKeysUpdate,
TAuditLogs,
TAuditLogsInsert,
TAuditLogsUpdate,
TAuthTokens, TAuthTokens,
TAuthTokenSessions, TAuthTokenSessions,
TAuthTokenSessionsInsert, TAuthTokenSessionsInsert,
@@ -43,6 +46,9 @@ import {
TOrganizations, TOrganizations,
TOrganizationsInsert, TOrganizationsInsert,
TOrganizationsUpdate, TOrganizationsUpdate,
TOrgBots,
TOrgBotsInsert,
TOrgBotsUpdate,
TOrgMemberships, TOrgMemberships,
TOrgMembershipsInsert, TOrgMembershipsInsert,
TOrgMembershipsUpdate, TOrgMembershipsUpdate,
@@ -67,6 +73,9 @@ import {
TProjects, TProjects,
TProjectsInsert, TProjectsInsert,
TProjectsUpdate, TProjectsUpdate,
TSamlConfigs,
TSamlConfigsInsert,
TSamlConfigsUpdate,
TSapApprovers, TSapApprovers,
TSapApproversInsert, TSapApproversInsert,
TSapApproversUpdate, TSapApproversUpdate,
@@ -357,6 +366,13 @@ declare module "knex/types/tables" {
TSecretSnapshotFoldersInsert, TSecretSnapshotFoldersInsert,
TSecretSnapshotFoldersUpdate TSecretSnapshotFoldersUpdate
>; >;
[TableName.SamlConfig]: Knex.CompositeTableType<
TSamlConfigs,
TSamlConfigsInsert,
TSamlConfigsUpdate
>;
[TableName.OrgBot]: Knex.CompositeTableType<TOrgBots, TOrgBotsInsert, TOrgBotsUpdate>;
[TableName.AuditLog]: Knex.CompositeTableType<TAuditLogs, TAuditLogsInsert, TAuditLogsUpdate>;
// Junction tables // Junction tables
[TableName.JnSecretTag]: Knex.CompositeTableType< [TableName.JnSecretTag]: Knex.CompositeTableType<
TSecretTagJunction, TSecretTagJunction,
@@ -22,7 +22,7 @@ export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.Secret))) { if (!(await knex.schema.hasTable(TableName.Secret))) {
await knex.schema.createTable(TableName.Secret, (t) => { await knex.schema.createTable(TableName.Secret, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); 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.string("type").notNullable().defaultTo(SecretType.Shared);
// t.text("secretKeyHash").notNullable(); // t.text("secretKeyHash").notNullable();
// t.text("secretValueHash"); // t.text("secretValueHash");
@@ -7,7 +7,7 @@ export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.SecretVersion))) { if (!(await knex.schema.hasTable(TableName.SecretVersion))) {
await knex.schema.createTable(TableName.SecretVersion, (t) => { await knex.schema.createTable(TableName.SecretVersion, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); 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.string("type").notNullable().defaultTo(SecretType.Shared);
t.text("secretBlindIndex").notNullable(); t.text("secretBlindIndex").notNullable();
t.text("secretKeyCiphertext").notNullable(); t.text("secretKeyCiphertext").notNullable();
@@ -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);
}
@@ -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);
}
@@ -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
View 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>>;
+3
View File
@@ -1,4 +1,5 @@
export * from "./api-keys"; export * from "./api-keys";
export * from "./audit-logs";
export * from "./auth-token-sessions"; export * from "./auth-token-sessions";
export * from "./auth-tokens"; export * from "./auth-tokens";
export * from "./backup-private-key"; export * from "./backup-private-key";
@@ -12,6 +13,7 @@ export * from "./incident-contacts";
export * from "./integration-auths"; export * from "./integration-auths";
export * from "./integrations"; export * from "./integrations";
export * from "./models"; export * from "./models";
export * from "./org-bots";
export * from "./org-memberships"; export * from "./org-memberships";
export * from "./org-roles"; export * from "./org-roles";
export * from "./organizations"; export * from "./organizations";
@@ -23,6 +25,7 @@ export * from "./project-roles";
export * from "./projects"; export * from "./projects";
export * from "./sa-request-secret-tags"; export * from "./sa-request-secret-tags";
export * from "./sa-request-secrets"; export * from "./sa-request-secrets";
export * from "./saml-configs";
export * from "./sap-approvers"; export * from "./sap-approvers";
export * from "./sar-reviewers"; export * from "./sar-reviewers";
export * from "./secret-approval-policies"; export * from "./secret-approval-policies";
+3
View File
@@ -9,6 +9,7 @@ export enum TableName {
Organization = "organizations", Organization = "organizations",
OrgMembership = "org_memberships", OrgMembership = "org_memberships",
OrgRoles = "org_roles", OrgRoles = "org_roles",
OrgBot = "org_bots",
IncidentContact = "incident_contacts", IncidentContact = "incident_contacts",
UserAction = "user_actions", UserAction = "user_actions",
SuperAdmin = "super_admin", SuperAdmin = "super_admin",
@@ -47,6 +48,8 @@ export enum TableName {
SarSecretTag = "sa_request_secret_tags", SarSecretTag = "sa_request_secret_tags",
SecretRotation = "secret_rotations", SecretRotation = "secret_rotations",
SecretRotationOutput = "secret_rotation_outputs", SecretRotationOutput = "secret_rotation_outputs",
SamlConfig = "saml_configs",
AuditLog = "audit_logs",
// junction tables // junction tables
JnSecretTag = "secret_tag_junction", JnSecretTag = "secret_tag_junction",
JnSecretVersionTag = "secret_version_tag_junction" JnSecretVersionTag = "secret_version_tag_junction"
+31
View 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
View 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>>;
+1 -1
View File
@@ -9,7 +9,7 @@ import { TImmutableDBKeys } from "./models";
export const SecretVersionsSchema = z.object({ export const SecretVersionsSchema = z.object({
id: z.string().uuid(), id: z.string().uuid(),
version: z.number().default(1).nullable().optional(), version: z.number().default(1),
type: z.string().default("shared"), type: z.string().default("shared"),
secretBlindIndex: z.string(), secretBlindIndex: z.string(),
secretKeyCiphertext: z.string(), secretKeyCiphertext: z.string(),
+1 -1
View File
@@ -9,7 +9,7 @@ import { TImmutableDBKeys } from "./models";
export const SecretsSchema = z.object({ export const SecretsSchema = z.object({
id: z.string().uuid(), id: z.string().uuid(),
version: z.number().default(1).nullable().optional(), version: z.number().default(1),
type: z.string().default("shared"), type: z.string().default("shared"),
secretBlindIndex: z.string(), secretBlindIndex: z.string(),
secretKeyCiphertext: z.string(), secretKeyCiphertext: z.string(),
+2
View File
@@ -1,6 +1,7 @@
import { registerOrgRoleRouter } from "./org-role-router"; import { registerOrgRoleRouter } from "./org-role-router";
import { registerProjectRoleRouter } from "./project-role-router"; import { registerProjectRoleRouter } from "./project-role-router";
import { registerProjectRouter } from "./project-router"; import { registerProjectRouter } from "./project-router";
import { registerSamlRouter } from "./saml-router";
import { registerSecretApprovalPolicyRouter } from "./secret-approval-policy-router"; import { registerSecretApprovalPolicyRouter } from "./secret-approval-policy-router";
import { registerSecretApprovalRequestRouter } from "./secret-approval-request-router"; import { registerSecretApprovalRequestRouter } from "./secret-approval-request-router";
import { registerSecretRotationProviderRouter } from "./secret-rotation-provider-router"; import { registerSecretRotationProviderRouter } from "./secret-rotation-provider-router";
@@ -25,5 +26,6 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => {
await server.register(registerSecretRotationProviderRouter, { await server.register(registerSecretRotationProviderRouter, {
prefix: "/secret-rotation-providers" prefix: "/secret-rotation-providers"
}); });
await server.register(registerSamlRouter, { prefix: "/sso" });
await server.register(registerSecretRotationRouter, { prefix: "/secret-rotations" }); await server.register(registerSecretRotationRouter, { prefix: "/secret-rotations" });
}; };
+72 -1
View File
@@ -1,6 +1,7 @@
import { z } from "zod"; 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 { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
@@ -65,4 +66,74 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
return { count }; 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
View 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, SecretsSchema,
SecretVersionsSchema SecretVersionsSchema
} from "@app/db/schemas"; } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { import {
ApprovalStatus, ApprovalStatus,
RequestState RequestState
@@ -230,6 +231,23 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
approvalId: req.params.id, approvalId: req.params.id,
status: req.body.status 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 }; return { approval };
} }
}); });
@@ -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 };
};
@@ -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
};
};
@@ -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
};
};
@@ -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;
@@ -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;
};
@@ -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
};
};
@@ -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: [ childrenMapper: [
{ {
key: "reviewerMemberId", key: "reviewerMemberId",
label: "reviewers", label: "reviewers" as const,
mapper: ({ reviewerMemberId: member, reviewerStatus: status }) => mapper: ({ reviewerMemberId: member, reviewerStatus: status }) =>
member ? { member, status } : undefined member ? { member, status } : undefined
}, },
{ key: "approverId", label: "approvers", mapper: ({ approverId }) => approverId } { key: "approverId", label: "approvers" as const, mapper: ({ approverId }) => approverId }
] as const ]
}); });
if (!formatedDoc?.[0]) return; if (!formatedDoc?.[0]) return;
return { return {
@@ -213,12 +213,12 @@ export const secretApprovalRequestDalFactory = (db: TDbClient) => {
childrenMapper: [ childrenMapper: [
{ {
key: "reviewerMemberId", key: "reviewerMemberId",
label: "reviewers", label: "reviewers" as const,
mapper: ({ reviewerMemberId: member, reviewerStatus: s }) => mapper: ({ reviewerMemberId: member, reviewerStatus: s }) =>
member ? { member, status: s } : undefined member ? { member, status: s } : undefined
}, },
{ key: "approverId", label: "approvers", mapper: ({ approverId }) => approverId } { key: "approverId", label: "approvers" as const, mapper: ({ approverId }) => approverId }
] as const ]
}); });
return formatedDoc.map((el) => ({ return formatedDoc.map((el) => ({
...el, ...el,
@@ -181,7 +181,7 @@ export const secretApprovalRequestServiceFactory = ({
status, status,
statusChangeBy: membership.id statusChangeBy: membership.id
}); });
return updatedRequest; return { ...secretApprovalRequest, ...updatedRequest };
}; };
const mergeSecretApprovalRequest = async ({ const mergeSecretApprovalRequest = async ({
@@ -61,7 +61,7 @@ export const secretRotationDalFactory = (db: TDbClient) => {
childrenMapper: [ childrenMapper: [
{ {
key: "secId", key: "secId",
label: "outputs", label: "outputs" as const,
mapper: ({ mapper: ({
secId, secId,
outputKey, outputKey,
@@ -92,7 +92,7 @@ export const secretRotationDalFactory = (db: TDbClient) => {
} }
}) })
} }
] as const ]
}); });
} catch (error) { } catch (error) {
throw new DatabaseError({ error, name: "SecretRotationFind" }); throw new DatabaseError({ error, name: "SecretRotationFind" });
@@ -7,7 +7,8 @@ import {
TSecretFolderVersions, TSecretFolderVersions,
TSecretSnapshotFolders, TSecretSnapshotFolders,
TSecretSnapshots, TSecretSnapshots,
TSecretVersions} from "@app/db/schemas"; TSecretVersions
} from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors"; import { DatabaseError } from "@app/lib/errors";
import { ormify, selectAllTableCols, sqlNestRelationships } from "@app/lib/knex"; import { ormify, selectAllTableCols, sqlNestRelationships } from "@app/lib/knex";
@@ -109,15 +110,15 @@ export const snapshotDalFactory = (db: TDbClient) => {
childrenMapper: [ childrenMapper: [
{ {
key: "id", key: "id",
label: "secretVersions", label: "secretVersions" as const,
mapper: (el) => SecretVersionsSchema.parse(el) mapper: (el) => SecretVersionsSchema.parse(el)
}, },
{ {
key: "folderVerId", key: "folderVerId",
label: "folderVersion", label: "folderVersion" as const,
mapper: ({ folderVerId: id, folderVerName: name }) => ({ id, name }) mapper: ({ folderVerId: id, folderVerName: name }) => ({ id, name })
} }
] as const ]
})?.[0]; })?.[0];
} catch (error) { } catch (error) {
throw new DatabaseError({ error, name: "FindSecretSnapshotDataById" }); throw new DatabaseError({ error, name: "FindSecretSnapshotDataById" });
@@ -237,7 +238,7 @@ export const snapshotDalFactory = (db: TDbClient) => {
childrenMapper: [ childrenMapper: [
{ {
key: "id", key: "id",
label: "secretVersions", label: "secretVersions" as const,
mapper: (el) => ({ mapper: (el) => ({
...SecretVersionsSchema.parse(el), ...SecretVersionsSchema.parse(el),
latestSecretVersion: el.latestSecretVersion latestSecretVersion: el.latestSecretVersion
@@ -245,14 +246,14 @@ export const snapshotDalFactory = (db: TDbClient) => {
}, },
{ {
key: "folderVerId", key: "folderVerId",
label: "folderVersion", label: "folderVersion" as const,
mapper: ({ folderVerId: id, folderVerName: name, latestFolderVersion }) => ({ mapper: ({ folderVerId: id, folderVerName: name, latestFolderVersion }) => ({
id, id,
name, name,
latestFolderVersion latestFolderVersion
}) })
} }
] as const ]
}); });
return formated; return formated;
} catch (error) { } catch (error) {
-190
View File
@@ -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();
+2
View File
@@ -122,6 +122,8 @@ export const decryptAsymmetric = ({
return naclUtils.encodeUTF8(plaintext); return naclUtils.encodeUTF8(plaintext);
}; };
export const generateSymmetricKey = (size = 32) => crypto.randomBytes(size).toString("base64");
export const generateAsymmetricKeyPair = () => { export const generateAsymmetricKeyPair = () => {
const pair = nacl.box.keyPair(); const pair = nacl.box.keyPair();
+20
View File
@@ -15,3 +15,23 @@ export const pick = <T extends object, TKeys extends keyof T>(
{} as Pick<T, TKeys> {} 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);
};
+2 -2
View File
@@ -3,12 +3,12 @@ import { Tables } from "knex/types/tables";
export const selectAllTableCols = <Tname extends keyof Tables>(tableName: Tname) => export const selectAllTableCols = <Tname extends keyof Tables>(tableName: Tname) =>
`${tableName}.*` as keyof Tables[Tname]["base"]; `${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; const copy = val;
Object.entries(copy).forEach(([key, value]) => { Object.entries(copy).forEach(([key, value]) => {
if (typeof value === "undefined") { if (typeof value === "undefined") {
delete copy[key as keyof T]; delete copy[key as keyof T];
} }
}); });
return copy; return copy as Exclude<T, undefined>;
}; };
+11 -3
View File
@@ -1,12 +1,16 @@
import { Job, JobsOptions, Queue, Worker, WorkerListener } from "bullmq"; import { Job, JobsOptions, Queue, Worker, WorkerListener } from "bullmq";
import Redis from "ioredis"; import Redis from "ioredis";
import { TCreateAuditLogDTO } from "@app/ee/services/audit-log/audit-log-types";
export enum QueueName { export enum QueueName {
SecretRotation = "secret-rotation" SecretRotation = "secret-rotation",
AuditLog = "audit-log"
} }
export enum QueueJobs { export enum QueueJobs {
SecretRotation = "secret-rotation-job" SecretRotation = "secret-rotation-job",
AuditLog = "audit-log-job"
} }
export type TQueueJobTypes = { export type TQueueJobTypes = {
@@ -14,6 +18,10 @@ export type TQueueJobTypes = {
payload: { rotationId: string }; payload: { rotationId: string };
name: QueueJobs.SecretRotation; name: QueueJobs.SecretRotation;
}; };
[QueueName.AuditLog]: {
name: QueueJobs.AuditLog;
payload: TCreateAuditLogDTO;
};
}; };
export type TQueueServiceFactory = ReturnType<typeof queueServiceFactory>; export type TQueueServiceFactory = ReturnType<typeof queueServiceFactory>;
@@ -65,7 +73,7 @@ export const queueServiceFactory = (redisUrl: string) => {
name: T, name: T,
job: TQueueJobTypes[T]["name"], job: TQueueJobTypes[T]["name"],
data: TQueueJobTypes[T]["payload"], data: TQueueJobTypes[T]["payload"],
opts: JobsOptions & { jobId: string } opts: JobsOptions & { jobId?: string }
) => { ) => {
const q = queueContainer[name]; const q = queueContainer[name];
await q.add(job, data, opts); await q.add(job, data, opts);
@@ -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;
});
});
+33 -5
View File
@@ -2,8 +2,13 @@ import { Knex } from "knex";
import { z } from "zod"; import { z } from "zod";
import { registerV1EERoutes } from "@app/ee/routes/v1"; 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 { permissionDalFactory } from "@app/ee/services/permission/permission-dal";
import { permissionServiceFactory } from "@app/ee/services/permission/permission-service"; 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 { 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 { 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"; 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 { integrationAuthDalFactory } from "@app/services/integration-auth/integration-auth-dal";
import { integrationAuthServiceFactory } from "@app/services/integration-auth/integration-auth-service"; import { integrationAuthServiceFactory } from "@app/services/integration-auth/integration-auth-service";
import { incidentContactDalFactory } from "@app/services/org/incident-contacts-dal"; 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 { orgDalFactory } from "@app/services/org/org-dal";
import { orgRoleDalFactory } from "@app/services/org/org-role-dal"; import { orgRoleDalFactory } from "@app/services/org/org-role-dal";
import { orgRoleServiceFactory } from "@app/services/org/org-role-service"; 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 { webhookDalFactory } from "@app/services/webhook/webhook-dal";
import { webhookServiceFactory } from "@app/services/webhook/webhook-service"; import { webhookServiceFactory } from "@app/services/webhook/webhook-service";
import { injectAuditLogInfo } from "../plugins/audit-log";
import { injectIdentity } from "../plugins/auth/inject-identity"; import { injectIdentity } from "../plugins/auth/inject-identity";
import { injectPermission } from "../plugins/auth/inject-permission"; import { injectPermission } from "../plugins/auth/inject-permission";
import { registerV1Routes } from "./v1"; import { registerV1Routes } from "./v1";
@@ -99,6 +106,7 @@ export const registerRoutes = async (
const authDal = authDalFactory(db); const authDal = authDalFactory(db);
const authTokenDal = tokenDalFactory(db); const authTokenDal = tokenDalFactory(db);
const orgDal = orgDalFactory(db); const orgDal = orgDalFactory(db);
const orgBotDal = orgBotDalFactory(db);
const incidentContactDal = incidentContactDalFactory(db); const incidentContactDal = incidentContactDalFactory(db);
const orgRoleDal = orgRoleDalFactory(db); const orgRoleDal = orgRoleDalFactory(db);
const superAdminDal = superAdminDalFactory(db); const superAdminDal = superAdminDalFactory(db);
@@ -132,8 +140,11 @@ export const registerRoutes = async (
const identityUaDal = identityUaDalFactory(db); const identityUaDal = identityUaDalFactory(db);
const identityUaClientSecretDal = identityUaClientSecretDalFactory(db); const identityUaClientSecretDal = identityUaClientSecretDalFactory(db);
const auditLogDal = auditLogDalFactory(db);
// ee db layer ops // ee db layer ops
const permissionDal = permissionDalFactory(db); const permissionDal = permissionDalFactory(db);
const samlConfigDal = samlConfigDalFactory(db);
const sapApproverDal = sapApproverDalFactory(db); const sapApproverDal = sapApproverDalFactory(db);
const secretApprovalPolicyDal = secretApprovalPolicyDalFactory(db); const secretApprovalPolicyDal = secretApprovalPolicyDalFactory(db);
const secretApprovalRequestDal = secretApprovalRequestDalFactory(db); const secretApprovalRequestDal = secretApprovalRequestDalFactory(db);
@@ -146,6 +157,8 @@ export const registerRoutes = async (
const snapshotFolderDal = snapshotFolderDalFactory(db); const snapshotFolderDal = snapshotFolderDalFactory(db);
const permissionService = permissionServiceFactory({ permissionDal, orgRoleDal, projectRoleDal }); const permissionService = permissionServiceFactory({ permissionDal, orgRoleDal, projectRoleDal });
const auditLogQueue = auditLogQueueServiceFactory({ auditLogDal, queueService });
const auditLogService = auditLogServiceFactory({ auditLogDal, permissionService, auditLogQueue });
const sapService = secretApprovalPolicyServiceFactory({ const sapService = secretApprovalPolicyServiceFactory({
projectMembershipDal, projectMembershipDal,
projectEnvDal, projectEnvDal,
@@ -153,6 +166,13 @@ export const registerRoutes = async (
permissionService, permissionService,
secretApprovalPolicyDal secretApprovalPolicyDal
}); });
const samlService = samlConfigServiceFactory({
permissionService,
orgBotDal,
orgDal,
userDal,
samlConfigDal
});
const sarService = secretApprovalRequestServiceFactory({ const sarService = secretApprovalRequestServiceFactory({
permissionService, permissionService,
folderDal, folderDal,
@@ -180,7 +200,8 @@ export const registerRoutes = async (
incidentContactDal, incidentContactDal,
tokenService, tokenService,
smtpService, smtpService,
userDal userDal,
orgBotDal
}); });
const signupService = authSignupServiceFactory({ const signupService = authSignupServiceFactory({
tokenService, tokenService,
@@ -354,7 +375,9 @@ export const registerRoutes = async (
secretApprovalPolicy: sapService, secretApprovalPolicy: sapService,
secretApprovalRequest: sarService, secretApprovalRequest: sarService,
secretRotation: secretRotationService, secretRotation: secretRotationService,
snapshot: snapshotService snapshot: snapshotService,
saml: samlService,
auditLog: auditLogService
}); });
server.decorate<FastifyZodProvider["store"]>("store", { server.decorate<FastifyZodProvider["store"]>("store", {
@@ -363,6 +386,7 @@ export const registerRoutes = async (
await server.register(injectIdentity); await server.register(injectIdentity);
await server.register(injectPermission); await server.register(injectPermission);
await server.register(injectAuditLogInfo);
server.route({ server.route({
url: "/status", url: "/status",
@@ -394,9 +418,13 @@ export const registerRoutes = async (
}); });
// register routes for v1 // 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(registerV2Routes, { prefix: "/v2" });
await server.register(registerV3Routes, { prefix: "/v3" }); await server.register(registerV3Routes, { prefix: "/v3" });
await server.register(registerV1EERoutes, { prefix: "/ee/v1" });
}; };
@@ -1,6 +1,7 @@
import { z } from "zod"; import { z } from "zod";
import { IdentitiesSchema, OrgMembershipRole } from "@app/db/schemas"; 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 { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
@@ -28,6 +29,19 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
...req.body, ...req.body,
orgId: req.body.organizationId 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 }; return { identity };
} }
}); });
@@ -57,6 +71,19 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
id: req.params.identityId, id: req.params.identityId,
...req.body ...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 }; return { identity };
} }
}); });
@@ -81,6 +108,17 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id, actorId: req.permission.id,
id: req.params.identityId 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 }; return { identity };
} }
}); });
+109 -9
View File
@@ -1,8 +1,10 @@
import { z } from "zod"; import { z } from "zod";
import { IdentityUaClientSecretsSchema, IdentityUniversalAuthsSchema } from "@app/db/schemas"; 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 { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
export const sanitizedClientSecretSchema = IdentityUaClientSecretsSchema.pick({ export const sanitizedClientSecretSchema = IdentityUaClientSecretsSchema.pick({
id: true, id: true,
@@ -36,10 +38,21 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
} }
}, },
handler: async (req) => { handler: async (req) => {
const { identityUa, accessToken } = await server.services.identityUa.login( const { identityUa, accessToken, identityAccessToken, validClientSecretInfo } =
req.body.clientId, await server.services.identityUa.login(req.body.clientId, req.body.clientSecret);
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 { return {
accessToken, accessToken,
tokenType: "Bearer" as const, tokenType: "Bearer" as const,
@@ -102,6 +115,24 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
...req.body, ...req.body,
identityId: req.params.identityId 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 }; return { identityUniversalAuth };
} }
}); });
@@ -152,6 +183,25 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
...req.body, ...req.body,
identityId: req.params.identityId 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 }; return { identityUniversalAuth };
} }
}); });
@@ -176,6 +226,18 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id, actorId: req.permission.id,
identityId: req.params.identityId 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 }; return { identityUniversalAuth };
} }
}); });
@@ -201,13 +263,26 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
} }
}, },
handler: async (req) => { handler: async (req) => {
const { clientSecret, clientSecretData } = const { clientSecret, clientSecretData, orgId } =
await server.services.identityUa.createUaClientSecret({ await server.services.identityUa.createUaClientSecret({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
identityId: req.params.identityId, identityId: req.params.identityId,
...req.body ...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 }; return { clientSecret, clientSecretData };
} }
}); });
@@ -227,10 +302,22 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
} }
}, },
handler: async (req) => { handler: async (req) => {
const clientSecretData = await server.services.identityUa.getUaClientSecrets({ const { clientSecrets: clientSecretData, orgId } =
actor: req.permission.type, await server.services.identityUa.getUaClientSecrets({
actorId: req.permission.id, actor: req.permission.type,
identityId: req.params.identityId 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 }; return { clientSecretData };
} }
@@ -258,6 +345,19 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
identityId: req.params.identityId, identityId: req.params.identityId,
clientSecretId: req.params.clientSecretId 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 }; return { clientSecretData };
} }
}); });
@@ -1,5 +1,6 @@
import { z } from "zod"; 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 { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
@@ -77,6 +78,18 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
actor: req.permission.type, actor: req.permission.type,
id: req.params.integrationAuthId 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 }; return { integrationAuth };
} }
}); });
@@ -85,6 +98,45 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
url: "/oauth-token", url: "/oauth-token",
method: "POST", method: "POST",
onRequest: verifyAuth([AuthMode.JWT]), 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: { schema: {
body: z.object({ body: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
@@ -108,6 +160,17 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
projectId: req.body.workspaceId, projectId: req.body.workspaceId,
...req.body ...req.body
}); });
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: req.body.workspaceId,
event: {
type: EventType.AUTHORIZE_INTEGRATION,
metadata: {
integration: integrationAuth.integration
}
}
});
return { integrationAuth }; return { integrationAuth };
} }
}); });
@@ -1,6 +1,8 @@
import { z } from "zod"; import { z } from "zod";
import { IntegrationsSchema } from "@app/db/schemas"; 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 { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
@@ -45,11 +47,33 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
}, },
onRequest: verifyAuth([AuthMode.JWT]), onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => { handler: async (req) => {
const integration = await server.services.integration.createIntegration({ const { integration, integrationAuth } = await server.services.integration.createIntegration({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
...req.body ...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 }; return { integration };
} }
}); });
@@ -108,6 +132,29 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
actor: req.permission.type, actor: req.permission.type,
id: req.params.integrationId 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 }; return { integration };
} }
}); });
@@ -1,6 +1,7 @@
import { z } from "zod"; import { z } from "zod";
import { ProjectEnvironmentsSchema } from "@app/db/schemas"; 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 { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
@@ -32,6 +33,18 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
projectId: req.params.workspaceId, projectId: req.params.workspaceId,
...req.body ...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 { return {
message: "Successfully created new environment", message: "Successfully created new environment",
workspace: req.params.workspaceId, workspace: req.params.workspaceId,
@@ -62,13 +75,28 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
handler: async (req) => { handler: async (req) => {
const environment = await server.services.projectEnv.updateEnvironment({ const { environment, old } = await server.services.projectEnv.updateEnvironment({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
projectId: req.params.workspaceId, projectId: req.params.workspaceId,
id: req.params.id, id: req.params.id,
...req.body ...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 { return {
message: "Successfully updated environment", message: "Successfully updated environment",
workspace: req.params.workspaceId, workspace: req.params.workspaceId,
@@ -101,6 +129,19 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
projectId: req.params.workspaceId, projectId: req.params.workspaceId,
id: req.params.id 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 { return {
message: "Successfully deleted environment", message: "Successfully deleted environment",
workspace: req.params.workspaceId, workspace: req.params.workspaceId,
@@ -1,6 +1,12 @@
import { z } from "zod"; 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 { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; 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({ server.route({
url: "/:workspaceId/memberships/:membershipId", url: "/:workspaceId/memberships/:membershipId",
method: "PATCH", method: "PATCH",
@@ -68,6 +123,20 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
membershipId: req.params.membershipId, membershipId: req.params.membershipId,
role: req.body.role 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 }; return { membership };
} }
}); });
@@ -94,6 +163,18 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
projectId: req.params.workspaceId, projectId: req.params.workspaceId,
membershipId: req.params.membershipId 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 }; return { membership };
} }
}); });
@@ -8,6 +8,7 @@ import {
UserEncryptionKeysSchema, UserEncryptionKeysSchema,
UsersSchema UsersSchema
} from "@app/db/schemas"; } 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 { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
@@ -265,6 +266,18 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
projectId: req.params.workspaceId, projectId: req.params.workspaceId,
email: req.body.email 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 }; return { invitee, latestKey };
} }
}); });
@@ -1,6 +1,7 @@
import { z } from "zod"; import { z } from "zod";
import { SecretFoldersSchema } from "@app/db/schemas"; 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 { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
@@ -28,6 +29,19 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
actor: req.permission.type, actor: req.permission.type,
...req.body ...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 }; return { folder };
} }
}); });
@@ -53,12 +67,26 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
handler: async (req) => { handler: async (req) => {
const folder = await server.services.folder.updateFolder({ const { folder, old } = await server.services.folder.updateFolder({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
...req.body, ...req.body,
id: req.params.folderId 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 }; return { folder };
} }
}); });
@@ -89,6 +117,19 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
...req.body, ...req.body,
id: req.params.folderId 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 }; return { folder };
} }
}); });
@@ -1,6 +1,7 @@
import { z } from "zod"; import { z } from "zod";
import { SecretImportsSchema, SecretsSchema } from "@app/db/schemas"; 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 { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
@@ -37,6 +38,22 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
...req.body, ...req.body,
data: req.body.import 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 }; return { message: "Successfully created secret import", secretImport };
} }
}); });
@@ -78,6 +95,22 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
...req.body, ...req.body,
data: req.body.import 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 }; return { message: "Successfully updated secret import", secretImport };
} }
}); });
@@ -113,6 +146,22 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
id: req.params.secretImportId, id: req.params.secretImportId,
...req.body ...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 }; return { message: "Successfully deleted secret import", secretImport };
} }
}); });
@@ -146,6 +195,19 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
actor: req.permission.type, actor: req.permission.type,
...req.query ...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 }; return { message: "Successfully fetched secret imports", secretImports };
} }
}); });
@@ -1,6 +1,7 @@
import { z } from "zod"; import { z } from "zod";
import { WebhooksSchema } from "@app/db/schemas"; 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 { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
@@ -9,10 +10,10 @@ export const sanitizedWebhookSchema = WebhooksSchema.omit({
iv: true, iv: true,
tag: true, tag: true,
algorithm: true, algorithm: true,
keyEncoding: true, keyEncoding: true
}).merge( }).merge(
z.object({ z.object({
projectId:z.string(), projectId: z.string(),
environment: z.object({ environment: z.object({
id: z.string(), id: z.string(),
name: z.string(), name: z.string(),
@@ -48,6 +49,22 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
projectId: req.body.workspaceId, projectId: req.body.workspaceId,
...req.body ...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 }; return { message: "Successfully created webhook", webhook };
} }
}); });
@@ -77,6 +94,22 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
id: req.params.webhookId, id: req.params.webhookId,
isDisabled: req.body.isDisabled 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 }; return { message: "Successfully updated webhook", webhook };
} }
}); });
@@ -96,6 +129,22 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id, actorId: req.permission.id,
id: req.params.webhookId 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 }; return { message: "Successfully deleted webhook", webhook };
} }
}); });
@@ -1,6 +1,7 @@
import { z } from "zod"; import { z } from "zod";
import { ProjectKeysSchema } from "@app/db/schemas"; 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 { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
@@ -30,6 +31,17 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
projectId: req.params.workspaceId 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; return key;
} }
}); });
@@ -1,6 +1,7 @@
import { z } from "zod"; import { z } from "zod";
import { ServiceTokensSchema } from "@app/db/schemas"; 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 { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
@@ -68,6 +69,18 @@ export const registerServiceTokenRouter = async (server: FastifyZodProvider) =>
...req.body, ...req.body,
projectId: req.body.workspaceId 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 }; return { serviceToken: token, serviceTokenData: serviceToken };
} }
}); });
@@ -92,6 +105,19 @@ export const registerServiceTokenRouter = async (server: FastifyZodProvider) =>
actor: req.permission.type, actor: req.permission.type,
id: req.params.serviceTokenId 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 }; return { serviceTokenData };
} }
}); });
@@ -1,6 +1,7 @@
import { z } from "zod"; import { z } from "zod";
import { SecretApprovalRequestsSchema, SecretsSchema, SecretType } from "@app/db/schemas"; 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 { CommitType } from "@app/ee/services/secret-approval-request/secret-approval-request-types";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { ActorType, AuthMode } from "@app/services/auth/auth-type"; import { ActorType, AuthMode } from "@app/services/auth/auth-type";
@@ -35,6 +36,19 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
path: req.query.secretPath 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 }; return { secrets };
} }
}); });
@@ -74,6 +88,20 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
type: req.query.type 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 }; 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 }; return { approval };
} }
} }
@@ -191,6 +232,20 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
metadata 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 }; 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 }; return { approval };
} }
} }
@@ -325,6 +393,20 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
newSecretName 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 }; 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 }; return { approval };
} }
} }
@@ -397,6 +492,20 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
secretId 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 }; return { secret };
} }
}); });
@@ -463,6 +572,19 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
[CommitType.Create]: inputSecrets.filter(({ type }) => type === "shared") [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 }; return { approval };
} }
} }
@@ -476,6 +598,22 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
secrets: inputSecrets 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 }; return { secrets };
} }
}); });
@@ -542,6 +680,19 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
[CommitType.Update]: inputSecrets.filter(({ type }) => type === "shared") [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 }; return { approval };
} }
} }
@@ -554,6 +705,22 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
secrets: inputSecrets 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 }; return { secrets };
} }
}); });
@@ -609,6 +776,18 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
[CommitType.Delete]: inputSecrets.filter(({ type }) => type === "shared") [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 }; return { approval };
} }
} }
@@ -621,6 +800,22 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
secrets: inputSecrets 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 }; return { secrets };
} }
}); });
+2 -1
View File
@@ -32,7 +32,8 @@ export enum AuthMode {
export enum ActorType { // would extend to AWS, Azure, ... export enum ActorType { // would extend to AWS, Azure, ...
USER = "user", // userIdentity USER = "user", // userIdentity
SERVICE = "service", SERVICE = "service",
IDENTITY = "identity" IDENTITY = "identity",
Machine = "machine"
} }
export type AuthModeJwtTokenPayload = { export type AuthModeJwtTokenPayload = {
@@ -132,7 +132,7 @@ export const identityUaServiceFactory = ({
: identityAccessToken.accessTokenMaxTTL : identityAccessToken.accessTokenMaxTTL
} }
); );
return { accessToken, identityUa }; return { accessToken, identityUa, validClientSecretInfo, identityAccessToken };
}; };
const attachUa = async ({ const attachUa = async ({
@@ -216,7 +216,7 @@ export const identityUaServiceFactory = ({
); );
return doc; return doc;
}); });
return identityUa; return { ...identityUa, orgId: identityMembershipOrg.orgId };
}; };
const updateUa = async ({ const updateUa = async ({
@@ -295,7 +295,7 @@ export const identityUaServiceFactory = ({
? JSON.stringify(reformattedAccessTokenTrustedIps) ? JSON.stringify(reformattedAccessTokenTrustedIps)
: undefined : undefined
}); });
return updatedUaAuth; return { ...updatedUaAuth, orgId: identityMembershipOrg.orgId };
}; };
const getIdentityUa = async ({ identityId, actorId, actor }: TGetUaDTO) => { const getIdentityUa = async ({ identityId, actorId, actor }: TGetUaDTO) => {
@@ -317,7 +317,7 @@ export const identityUaServiceFactory = ({
OrgPermissionActions.Read, OrgPermissionActions.Read,
OrgPermissionSubjects.Identity OrgPermissionSubjects.Identity
); );
return uaIdentityAuth; return { ...uaIdentityAuth, orgId: identityMembershipOrg.orgId };
}; };
const createUaClientSecret = async ({ const createUaClientSecret = async ({
@@ -375,7 +375,8 @@ export const identityUaServiceFactory = ({
return { return {
clientSecret, clientSecret,
clientSecretData: identityUaClientSecret, clientSecretData: identityUaClientSecret,
uaAuth: identityUniversalAuth uaAuth: identityUniversalAuth,
orgId: identityMembershipOrg.orgId
}; };
}; };
@@ -415,7 +416,7 @@ export const identityUaServiceFactory = ({
identityUAId: identityUniversalAuth.id, identityUAId: identityUniversalAuth.id,
isClientSecretRevoked: false isClientSecretRevoked: false
}); });
return clientSecrets; return { clientSecrets, orgId: identityMembershipOrg.orgId };
}; };
const revokeUaClientSecret = async ({ const revokeUaClientSecret = async ({
@@ -454,7 +455,7 @@ export const identityUaServiceFactory = ({
const clientSecret = await identityUaClientSecretDal.updateById(clientSecretId, { const clientSecret = await identityUaClientSecretDal.updateById(clientSecretId, {
isClientSecretRevoked: true isClientSecretRevoked: true
}); });
return clientSecret; return { ...clientSecret, identityId, orgId: identityMembershipOrg.orgId };
}; };
return { return {
@@ -111,8 +111,7 @@ export const identityServiceFactory = ({
return newIdentity; return newIdentity;
}); });
// TODO(akhilmhdh-pg): add audit log here return { ...identity, orgId: identityOrgMembership.orgId };
return identity;
}; };
const deleteIdentity = async ({ actorId, actor, id }: TDeleteIdentityDTO) => { const deleteIdentity = async ({ actorId, actor, id }: TDeleteIdentityDTO) => {
@@ -139,7 +138,7 @@ export const identityServiceFactory = ({
throw new ForbiddenRequestError({ message: "Failed to delete more privileged identity" }); throw new ForbiddenRequestError({ message: "Failed to delete more privileged identity" });
const deletedIdentity = await identityDal.deleteById(id); const deletedIdentity = await identityDal.deleteById(id);
return deletedIdentity; return { ...deletedIdentity, orgId: identityOrgMembership.orgId };
}; };
const listOrgIdentities = async ({ orgId, actor, actorId }: TOrgPermission) => { const listOrgIdentities = async ({ orgId, actor, actorId }: TOrgPermission) => {
@@ -1,3 +1,4 @@
import { IPType } from "@app/lib/ip";
import { TOrgPermission } from "@app/lib/types"; import { TOrgPermission } from "@app/lib/types";
export type TCreateIdentityDTO = { export type TCreateIdentityDTO = {
@@ -14,3 +15,9 @@ export type TUpdateIdentityDTO = {
export type TDeleteIdentityDTO = { export type TDeleteIdentityDTO = {
id: string; id: string;
} & Omit<TOrgPermission, "orgId">; } & Omit<TOrgPermission, "orgId">;
export interface TIdentityTrustedIp {
ipAddress: string;
type: IPType;
prefix: number;
}
@@ -998,7 +998,7 @@ export const integrationAuthServiceFactory = ({
}; };
return { return {
listIntegrationAuthByProjectId, listIntegrationAuthByProjectId,
getIntegrationOptions, getIntegrationOptions,
getIntegrationAuth, getIntegrationAuth,
oauthExchange, oauthExchange,
@@ -7,7 +7,7 @@ export type TGetIntegrationAuthDTO = {
export type TOauthExchangeDTO = { export type TOauthExchangeDTO = {
integration: string; integration: string;
code: string; code: string;
url: string; url?: string;
} & TProjectPermission; } & TProjectPermission;
export type TSaveIntegrationAccessTokenDTO = { export type TSaveIntegrationAccessTokenDTO = {
@@ -91,7 +91,7 @@ export const integrationServiceFactory = ({
}); });
// TODO(akhilmhdh-pg): audit log // TODO(akhilmhdh-pg): audit log
return integration; return { integration, integrationAuth };
}; };
const updateIntegration = async ({ const updateIntegration = async ({
@@ -150,7 +150,7 @@ export const integrationServiceFactory = ({
); );
const deletedIntegration = await integrationDal.deleteById(id); const deletedIntegration = await integrationDal.deleteById(id);
return deletedIntegration; return { ...integration, ...deletedIntegration };
}; };
const listIntegrationByProject = async ({ actor, actorId, projectId }: TProjectPermission) => { const listIntegrationByProject = async ({ actor, actorId, projectId }: TProjectPermission) => {
@@ -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;
};
+3 -10
View File
@@ -9,7 +9,7 @@ import {
TOrgMembershipsUpdate TOrgMembershipsUpdate
} from "@app/db/schemas"; } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors"; 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>; export type TOrgDalFactory = ReturnType<typeof orgDalFactory>;
@@ -110,14 +110,7 @@ export const orgDalFactory = (db: TDbClient) => {
// MEMBERSHIP OPERATIONS // MEMBERSHIP OPERATIONS
// -------------------- // --------------------
const findMembership = async (filter: Partial<TOrgMemberships>, tx?: Knex) => { const orgMembershipOrm = ormify(db, TableName.OrgMembership);
try {
const membership = await (tx || db)(TableName.OrgMembership).where(filter);
return membership;
} catch (error) {
throw new DatabaseError({ error, name: "Find org membership" });
}
};
const createMembership = async (data: TOrgMembershipsInsert, tx?: Knex) => { const createMembership = async (data: TOrgMembershipsInsert, tx?: Knex) => {
try { try {
@@ -175,7 +168,7 @@ export const orgDalFactory = (db: TDbClient) => {
create, create,
updateById, updateById,
deleteById, deleteById,
findMembership, findMembership: orgMembershipOrm.find,
createMembership, createMembership,
updateMembershipById, updateMembershipById,
deleteMembershipById, deleteMembershipById,
+42 -2
View File
@@ -8,6 +8,8 @@ import {
} from "@app/ee/services/permission/org-permission"; } from "@app/ee/services/permission/org-permission";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { getConfig } from "@app/lib/config/env"; 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 { BadRequestError, UnauthorizedError } from "@app/lib/errors";
import { isDisposableEmail } from "@app/lib/validator"; 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 { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
import { TUserDalFactory } from "../user/user-dal"; import { TUserDalFactory } from "../user/user-dal";
import { TIncidentContactsDalFactory } from "./incident-contacts-dal"; import { TIncidentContactsDalFactory } from "./incident-contacts-dal";
import { TOrgBotDalFactory } from "./org-bot-dal";
import { TOrgDalFactory } from "./org-dal"; import { TOrgDalFactory } from "./org-dal";
import { TOrgRoleDalFactory } from "./org-role-dal"; import { TOrgRoleDalFactory } from "./org-role-dal";
import { import {
@@ -28,6 +31,7 @@ import {
type TOrgServiceFactoryDep = { type TOrgServiceFactoryDep = {
orgDal: TOrgDalFactory; orgDal: TOrgDalFactory;
orgBotDal: TOrgBotDalFactory;
orgRoleDal: TOrgRoleDalFactory; orgRoleDal: TOrgRoleDalFactory;
userDal: TUserDalFactory; userDal: TUserDalFactory;
incidentContactDal: TIncidentContactsDalFactory; incidentContactDal: TIncidentContactsDalFactory;
@@ -45,7 +49,8 @@ export const orgServiceFactory = ({
incidentContactDal, incidentContactDal,
permissionService, permissionService,
smtpService, smtpService,
tokenService tokenService,
orgBotDal
}: TOrgServiceFactoryDep) => { }: TOrgServiceFactoryDep) => {
/* /*
* Get organization details by the organization id * Get organization details by the organization id
@@ -95,6 +100,23 @@ export const orgServiceFactory = ({
* Create organization * Create organization
* */ * */
const createOrganization = async (userId: string, orgName: string) => { 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 organization = await orgDal.transaction(async (tx) => {
const org = await orgDal.create({ name: orgName }, tx); const org = await orgDal.create({ name: orgName }, tx);
await orgDal.createMembership( await orgDal.createMembership(
@@ -106,6 +128,24 @@ export const orgServiceFactory = ({
}, },
tx tx
); );
await orgBotDal.create(
{
name: org.name,
publicKey,
privateKeyIV,
encryptedPrivateKey,
symmetricKeyIV,
symmetricKeyTag,
encryptedSymmetricKey,
symmetricKeyAlgorithm,
orgId: org.id,
privateKeyTag,
privateKeyAlgorithm,
privateKeyKeyEncoding,
symmetricKeyKeyEncoding
},
tx
);
return org; return org;
}); });
@@ -178,7 +218,7 @@ export const orgServiceFactory = ({
// Thus the signup flow is not needed anymore // Thus the signup flow is not needed anymore
const [inviteeMembership] = await orgDal.findMembership( const [inviteeMembership] = await orgDal.findMembership(
{ orgId, userId: inviteeUser.id }, { orgId, userId: inviteeUser.id },
tx { tx }
); );
if (inviteeMembership && inviteeMembership.status === OrgMembershipStatus.Accepted) { if (inviteeMembership && inviteeMembership.status === OrgMembershipStatus.Accepted) {
throw new BadRequestError({ throw new BadRequestError({
@@ -58,6 +58,9 @@ export const projectEnvServiceFactory = ({
ProjectPermissionSub.Environments ProjectPermissionSub.Environments
); );
const oldEnv = await projectEnvDal.findOne({ id, projectId });
if (!oldEnv) throw new BadRequestError({ message: "Environment not found" });
if (slug) { if (slug) {
const existingEnv = await projectEnvDal.findOne({ slug }); const existingEnv = await projectEnvDal.findOne({ slug });
if (existingEnv && existingEnv.id !== id) { if (existingEnv && existingEnv.id !== id) {
@@ -68,8 +71,8 @@ export const projectEnvServiceFactory = ({
} }
} }
const [env] = await projectEnvDal.update({ id, projectId }, { name, slug }); const env = await projectEnvDal.updateById(oldEnv.id, { name, slug });
return env; return { environment: env, old: oldEnv };
}; };
const deleteEnvironment = async ({ projectId, actor, actorId, id }: TDeleteEnvDTO) => { const deleteEnvironment = async ({ projectId, actor, actorId, id }: TDeleteEnvDTO) => {
@@ -8,6 +8,7 @@ import {
} from "@app/ee/services/permission/project-permission"; } from "@app/ee/services/permission/project-permission";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { groupBy } from "@app/lib/fn";
import { TOrgDalFactory } from "../org/org-dal"; import { TOrgDalFactory } from "../org/org-dal";
import { TProjectDalFactory } from "../project/project-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 { TUserDalFactory } from "../user/user-dal";
import { TProjectMembershipDalFactory } from "./project-membership-dal"; import { TProjectMembershipDalFactory } from "./project-membership-dal";
import { import {
TAddUsersToWorkspaceDTO,
TDeleteProjectMembershipDTO, TDeleteProjectMembershipDTO,
TGetProjectMembershipDTO, TGetProjectMembershipDTO,
TInviteUserToProjectDTO, TInviteUserToProjectDTO,
@@ -31,7 +33,7 @@ type TProjectMembershipServiceFactoryDep = {
projectRoleDal: Pick<TProjectRoleDalFactory, "findOne">; projectRoleDal: Pick<TProjectRoleDalFactory, "findOne">;
orgDal: Pick<TOrgDalFactory, "findMembership">; orgDal: Pick<TOrgDalFactory, "findMembership">;
projectDal: Pick<TProjectDalFactory, "findById">; projectDal: Pick<TProjectDalFactory, "findById">;
projectKeyDal: Pick<TProjectKeyDalFactory, "findLatestProjectKey" | "delete">; projectKeyDal: Pick<TProjectKeyDalFactory, "findLatestProjectKey" | "delete" | "insertMany">;
}; };
export type TProjectMembershipServiceFactory = ReturnType<typeof projectMembershipServiceFactory>; export type TProjectMembershipServiceFactory = ReturnType<typeof projectMembershipServiceFactory>;
@@ -122,6 +124,71 @@ export const projectMembershipServiceFactory = ({
return { invitee, latestKey }; 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 ({ const updateProjectMembership = async ({
actorId, actorId,
actor, actor,
@@ -186,6 +253,7 @@ export const projectMembershipServiceFactory = ({
getProjectMemberships, getProjectMemberships,
inviteUserToProject, inviteUserToProject,
updateProjectMembership, updateProjectMembership,
deleteProjectMembership deleteProjectMembership,
addUsersToProject
}; };
}; };
@@ -14,3 +14,11 @@ export type TUpdateProjectMembershipDTO = {
export type TDeleteProjectMembershipDTO = { export type TDeleteProjectMembershipDTO = {
membershipId: string; membershipId: string;
} & TProjectPermission; } & 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 }) 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 }); const env = await projectEnvDal.findOne({ projectId, slug: environment });
if (!env) if (!env)
throw new BadRequestError({ message: "Environment not found", name: "Create folder" }); throw new BadRequestError({ message: "Environment not found", name: "Update folder" });
const folder = await folderDal.findOne({ envId: env.id, id, parentId: parentFolder.id });
const folder = await folderDal.transaction(async (tx) => { if (!folder) throw new BadRequestError({ message: "Folder not found" });
const parentFolder = await folderDal.findBySecretPath(projectId, environment, path, tx);
if (!parentFolder) throw new BadRequestError({ message: "Secret path not found" });
const newFolder = await folderDal.transaction(async (tx) => {
const [doc] = await folderDal.update( const [doc] = await folderDal.update(
{ envId: env.id, id, parentId: parentFolder.id }, { envId: env.id, id, parentId: parentFolder.id },
{ name }, { name },
@@ -117,8 +119,8 @@ export const secretFolderServiceFactory = ({
return doc; return doc;
}); });
await snapshotService.performSnapshot(folder.parentId as string); await snapshotService.performSnapshot(newFolder.parentId as string);
return folder; return { folder: newFolder, old: folder };
}; };
const deleteFolder = async ({ const deleteFolder = async ({
@@ -6,8 +6,10 @@ import {
ProjectPermissionSub ProjectPermissionSub
} from "@app/ee/services/permission/project-permission"; } from "@app/ee/services/permission/project-permission";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { groupBy } from "@app/lib/fn";
import { TProjectEnvDalFactory } from "../project-env/project-env-dal"; import { TProjectEnvDalFactory } from "../project-env/project-env-dal";
import { TSecretDalFactory } from "../secret/secret-dal";
import { TSecretFolderDalFactory } from "../secret-folder/secret-folder-dal"; import { TSecretFolderDalFactory } from "../secret-folder/secret-folder-dal";
import { TSecretImportDalFactory } from "./secret-import-dal"; import { TSecretImportDalFactory } from "./secret-import-dal";
import { import {
@@ -17,8 +19,6 @@ import {
TGetSecretsFromImportDTO, TGetSecretsFromImportDTO,
TUpdateSecretImportDTO TUpdateSecretImportDTO
} from "./secret-import-types"; } from "./secret-import-types";
import { TSecretDalFactory } from "../secret/secret-dal";
import { groupBy } from "@app/lib/fn";
type TSecretImportServiceFactoryDep = { type TSecretImportServiceFactoryDep = {
secretImportDal: TSecretImportDalFactory; secretImportDal: TSecretImportDalFactory;
@@ -256,9 +256,10 @@ export const secretServiceFactory = ({
} }
const { keyName2BlindIndex } = await fnSecretBlindIndexCheck({ const { keyName2BlindIndex } = await fnSecretBlindIndexCheck({
inputSecrets: [{ secretName: inputSecret.secretName }], inputSecrets: [{ secretName: inputSecret.secretName, type: inputSecret.type as SecretType }],
folderId, folderId,
isNew: true, isNew: true,
userId: actorId,
blindIndexCfg blindIndexCfg
}); });
@@ -539,6 +540,7 @@ export const secretServiceFactory = ({
fnSecretBulkInsert({ fnSecretBulkInsert({
inputSecrets: inputSecrets.map(({ secretName, ...el }) => ({ inputSecrets: inputSecrets.map(({ secretName, ...el }) => ({
...el, ...el,
version:0,
secretBlindIndex: keyName2BlindIndex[secretName], secretBlindIndex: keyName2BlindIndex[secretName],
type: SecretType.Shared, type: SecretType.Shared,
algorithm: SecretEncryptionAlgo.AES_256_GCM, algorithm: SecretEncryptionAlgo.AES_256_GCM,
+6 -6
View File
@@ -18,7 +18,7 @@ export const useCreateProjectRole = () => {
return useMutation({ return useMutation({
mutationFn: ({ projectId, permissions, ...dto }: TCreateProjectRoleDTO) => mutationFn: ({ projectId, permissions, ...dto }: TCreateProjectRoleDTO) =>
apiRequest.post(`/api/ee/v1/workspace/${projectId}/roles`, { apiRequest.post(`/api/v1/workspace/${projectId}/roles`, {
...dto, ...dto,
permissions: permissions.length ? packRules(permissions) : [] permissions: permissions.length ? packRules(permissions) : []
}), }),
@@ -33,7 +33,7 @@ export const useUpdateProjectRole = () => {
return useMutation({ return useMutation({
mutationFn: ({ id, projectId, permissions, ...dto }: TUpdateProjectRoleDTO) => mutationFn: ({ id, projectId, permissions, ...dto }: TUpdateProjectRoleDTO) =>
apiRequest.patch(`/api/ee/v1/workspace/${projectId}/roles/${id}`, { apiRequest.patch(`/api/v1/workspace/${projectId}/roles/${id}`, {
...dto, ...dto,
permissions: permissions?.length ? packRules(permissions) : [] permissions: permissions?.length ? packRules(permissions) : []
}), }),
@@ -48,7 +48,7 @@ export const useDeleteProjectRole = () => {
return useMutation({ return useMutation({
mutationFn: ({ projectId, id }: TDeleteProjectRoleDTO) => mutationFn: ({ projectId, id }: TDeleteProjectRoleDTO) =>
apiRequest.delete(`/api/ee/v1/workspace/${projectId}/roles/${id}`, { apiRequest.delete(`/api/v1/workspace/${projectId}/roles/${id}`, {
data: { projectId } data: { projectId }
}), }),
onSuccess: (_, { projectId }) => { onSuccess: (_, { projectId }) => {
@@ -62,7 +62,7 @@ export const useCreateOrgRole = () => {
return useMutation({ return useMutation({
mutationFn: ({ orgId, permissions, ...dto }: TCreateOrgRoleDTO) => mutationFn: ({ orgId, permissions, ...dto }: TCreateOrgRoleDTO) =>
apiRequest.post(`/api/ee/v1/organization/${orgId}/roles`, { apiRequest.post(`/api/v1/organization/${orgId}/roles`, {
...dto, ...dto,
permissions: permissions.length ? packRules(permissions) : [] permissions: permissions.length ? packRules(permissions) : []
}), }),
@@ -77,7 +77,7 @@ export const useUpdateOrgRole = () => {
return useMutation({ return useMutation({
mutationFn: ({ id, orgId, permissions, ...dto }: TUpdateOrgRoleDTO) => mutationFn: ({ id, orgId, permissions, ...dto }: TUpdateOrgRoleDTO) =>
apiRequest.patch(`/api/ee/v1/organization/${orgId}/roles/${id}`, { apiRequest.patch(`/api/v1/organization/${orgId}/roles/${id}`, {
...dto, ...dto,
permissions: permissions?.length ? packRules(permissions) : [] permissions: permissions?.length ? packRules(permissions) : []
}), }),
@@ -92,7 +92,7 @@ export const useDeleteOrgRole = () => {
return useMutation({ return useMutation({
mutationFn: ({ orgId, id }: TDeleteOrgRoleDTO) => mutationFn: ({ orgId, id }: TDeleteOrgRoleDTO) =>
apiRequest.delete(`/api/ee/v1/organization/${orgId}/roles/${id}`, { apiRequest.delete(`/api/v1/organization/${orgId}/roles/${id}`, {
data: { orgId } data: { orgId }
}), }),
onSuccess: (_, { orgId }) => { onSuccess: (_, { orgId }) => {
+4 -4
View File
@@ -48,7 +48,7 @@ export const roleQueryKeys = {
const getProjectRoles = async (projectId: string) => { const getProjectRoles = async (projectId: string) => {
const { data } = await apiRequest.get<{ const { data } = await apiRequest.get<{
data: { roles: Array<Omit<TProjectRole, "permissions"> & { permissions: unknown }> }; 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 }) => ({ return data.data.roles.map(({ permissions, ...el }) => ({
...el, ...el,
permissions: unpackRules(permissions as PackRule<TProjectPermission>[]) permissions: unpackRules(permissions as PackRule<TProjectPermission>[])
@@ -65,7 +65,7 @@ export const useGetProjectRoles = (projectId: string) =>
const getOrgRoles = async (orgId: string) => { const getOrgRoles = async (orgId: string) => {
const { data } = await apiRequest.get<{ const { data } = await apiRequest.get<{
data: { roles: Array<Omit<TOrgRole, "permissions"> & { permissions: unknown }> }; 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 }) => ({ return data.data.roles.map(({ permissions, ...el }) => ({
...el, ...el,
permissions: unpackRules(permissions as PackRule<TPermission>[]) permissions: unpackRules(permissions as PackRule<TPermission>[])
@@ -85,7 +85,7 @@ const getUserOrgPermissions = async ({ orgId }: TGetUserOrgPermissionsDTO) => {
const { data } = await apiRequest.get<{ const { data } = await apiRequest.get<{
permissions: PackRule<RawRuleOf<MongoAbility<OrgPermissionSet>>>[]; permissions: PackRule<RawRuleOf<MongoAbility<OrgPermissionSet>>>[];
membership: OrgUser; membership: OrgUser;
}>(`/api/ee/v1/organization/${orgId}/permissions`); }>(`/api/v1/organization/${orgId}/permissions`);
return data; return data;
}; };
@@ -105,7 +105,7 @@ export const useGetUserOrgPermissions = ({ orgId }: TGetUserOrgPermissionsDTO) =
const getUserProjectPermissions = async ({ workspaceId }: TGetUserProjectPermissionDTO) => { const getUserProjectPermissions = async ({ workspaceId }: TGetUserProjectPermissionDTO) => {
const { data } = await apiRequest.get<{ const { data } = await apiRequest.get<{
data: { permissions: PackRule<RawRuleOf<MongoAbility<OrgPermissionSet>>>[] }; data: { permissions: PackRule<RawRuleOf<MongoAbility<OrgPermissionSet>>>[] };
}>(`/api/ee/v1/workspace/${workspaceId}/permissions`, {}); }>(`/api/v1/workspace/${workspaceId}/permissions`, {});
return data.data.permissions; return data.data.permissions;
}; };
@@ -10,7 +10,7 @@ export const useCreateSecretApprovalPolicy = () => {
return useMutation<{}, {}, TCreateSecretPolicyDTO>({ return useMutation<{}, {}, TCreateSecretPolicyDTO>({
mutationFn: async ({ environment, workspaceId, approvals, approvers, secretPath, name }) => { 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, environment,
workspaceId, workspaceId,
approvals, approvals,
@@ -31,7 +31,7 @@ export const useUpdateSecretApprovalPolicy = () => {
return useMutation<{}, {}, TUpdateSecretPolicyDTO>({ return useMutation<{}, {}, TUpdateSecretPolicyDTO>({
mutationFn: async ({ id, approvers, approvals, secretPath, name }) => { 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, approvals,
approvers, approvers,
secretPath, secretPath,
@@ -50,7 +50,7 @@ export const useDeleteSecretApprovalPolicy = () => {
return useMutation<{}, {}, TDeleteSecretPolicyDTO>({ return useMutation<{}, {}, TDeleteSecretPolicyDTO>({
mutationFn: async ({ id }) => { 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; return data;
}, },
onSuccess: (_, { workspaceId }) => { onSuccess: (_, { workspaceId }) => {
@@ -19,7 +19,7 @@ export const secretApprovalKeys = {
const fetchApprovalPolicies = async (workspaceId: string) => { const fetchApprovalPolicies = async (workspaceId: string) => {
const { data } = await apiRequest.get<{ approvals: TSecretApprovalPolicy[] }>( const { data } = await apiRequest.get<{ approvals: TSecretApprovalPolicy[] }>(
"/api/ee/v1/secret-approvals", "/api/v1/secret-approvals",
{ params: { workspaceId } } { params: { workspaceId } }
); );
return data.approvals; return data.approvals;
@@ -49,7 +49,7 @@ const fetchApprovalPolicyOfABoard = async (
secretPath: string secretPath: string
) => { ) => {
const { data } = await apiRequest.get<{ policy: TSecretApprovalPolicy }>( const { data } = await apiRequest.get<{ policy: TSecretApprovalPolicy }>(
"/api/ee/v1/secret-approvals/board", "/api/v1/secret-approvals/board",
{ params: { workspaceId, environment, secretPath } } { params: { workspaceId, environment, secretPath } }
); );
return data.policy || ""; return data.policy || "";
@@ -14,7 +14,7 @@ export const useUpdateSecretApprovalReviewStatus = () => {
return useMutation<{}, {}, TUpdateSecretApprovalReviewStatusDTO>({ return useMutation<{}, {}, TUpdateSecretApprovalReviewStatusDTO>({
mutationFn: async ({ id, status }) => { 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 status
}); });
return data; return data;
@@ -30,7 +30,7 @@ export const useUpdateSecretApprovalRequestStatus = () => {
return useMutation<{}, {}, TUpdateSecretApprovalRequestStatusDTO>({ return useMutation<{}, {}, TUpdateSecretApprovalRequestStatusDTO>({
mutationFn: async ({ id, status }) => { 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 status
}); });
return data; return data;
@@ -47,7 +47,7 @@ export const usePerformSecretApprovalRequestMerge = () => {
return useMutation<{}, {}, TPerformSecretApprovalRequestMerge>({ return useMutation<{}, {}, TPerformSecretApprovalRequestMerge>({
mutationFn: async ({ id }) => { 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; return data;
}, },
onSuccess: (_, { id, workspaceId }) => { onSuccess: (_, { id, workspaceId }) => {
@@ -96,7 +96,7 @@ const fetchSecretApprovalRequestList = async ({
offset offset
}: TGetSecretApprovalRequestList) => { }: TGetSecretApprovalRequestList) => {
const { data } = await apiRequest.get<{ approvals: TSecretApprovalRequest[] }>( const { data } = await apiRequest.get<{ approvals: TSecretApprovalRequest[] }>(
"/api/ee/v1/secret-approval-requests", "/api/v1/secret-approval-requests",
{ {
params: { params: {
workspaceId, workspaceId,
@@ -158,7 +158,7 @@ const fetchSecretApprovalRequestDetails = async ({
id id
}: Omit<TGetSecretApprovalRequestDetails, "decryptKey">) => { }: Omit<TGetSecretApprovalRequestDetails, "decryptKey">) => {
const { data } = await apiRequest.get<{ approval: TSecretApprovalRequest }>( const { data } = await apiRequest.get<{ approval: TSecretApprovalRequest }>(
`/api/ee/v1/secret-approval-requests/${id}` `/api/v1/secret-approval-requests/${id}`
); );
return data.approval; return data.approval;
@@ -197,7 +197,7 @@ export const useGetSecretApprovalRequestDetails = ({
const fetchSecretApprovalRequestCount = async ({ workspaceId }: TGetSecretApprovalRequestCount) => { const fetchSecretApprovalRequestCount = async ({ workspaceId }: TGetSecretApprovalRequestCount) => {
const { data } = await apiRequest.get<{ approvals: TSecretApprovalRequestCount }>( const { data } = await apiRequest.get<{ approvals: TSecretApprovalRequestCount }>(
"/api/ee/v1/secret-approval-requests/count", "/api/v1/secret-approval-requests/count",
{ params: { workspaceId } } { params: { workspaceId } }
); );
@@ -14,7 +14,7 @@ export const useCreateSecretRotation = () => {
return useMutation<{}, {}, TCreateSecretRotationDTO>({ return useMutation<{}, {}, TCreateSecretRotationDTO>({
mutationFn: async (dto) => { 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; return data;
}, },
onSuccess: (_, { workspaceId }) => { onSuccess: (_, { workspaceId }) => {
@@ -28,7 +28,7 @@ export const useDeleteSecretRotation = () => {
return useMutation<{}, {}, TDeleteSecretRotationDTO>({ return useMutation<{}, {}, TDeleteSecretRotationDTO>({
mutationFn: async (dto) => { 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; return data;
}, },
onSuccess: (_, { workspaceId }) => { onSuccess: (_, { workspaceId }) => {
@@ -42,7 +42,7 @@ export const useRestartSecretRotation = () => {
return useMutation<{}, {}, TRestartSecretRotationDTO>({ return useMutation<{}, {}, TRestartSecretRotationDTO>({
mutationFn: async (dto) => { 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; return data;
}, },
onSuccess: (_, { workspaceId }) => { onSuccess: (_, { workspaceId }) => {
@@ -25,7 +25,7 @@ export const secretRotationKeys = {
const fetchSecretRotationProviders = async ({ workspaceId }: TGetSecretRotationProviders) => { const fetchSecretRotationProviders = async ({ workspaceId }: TGetSecretRotationProviders) => {
const { data } = await apiRequest.get<TSecretRotationProviderList>( const { data } = await apiRequest.get<TSecretRotationProviderList>(
`/api/ee/v1/secret-rotation-providers/${workspaceId}` `/api/v1/secret-rotation-providers/${workspaceId}`
); );
return data; return data;
}; };
@@ -55,7 +55,7 @@ const fetchSecretRotations = async ({
workspaceId workspaceId
}: Omit<TGetSecretRotationList, "decryptFileKey">) => { }: Omit<TGetSecretRotationList, "decryptFileKey">) => {
const { data } = await apiRequest.get<{ secretRotations: TSecretRotation[] }>( const { data } = await apiRequest.get<{ secretRotations: TSecretRotation[] }>(
"/api/ee/v1/secret-rotations", "/api/v1/secret-rotations",
{ params: { workspaceId } } { params: { workspaceId } }
); );
return data.secretRotations; return data.secretRotations;
@@ -35,7 +35,7 @@ const fetchWorkspaceSnaphots = async ({
offset = 0 offset = 0
}: TGetSecretSnapshotsDTO & { offset: number }) => { }: TGetSecretSnapshotsDTO & { offset: number }) => {
const res = await apiRequest.get<{ secretSnapshots: TSecretSnapshot[] }>( const res = await apiRequest.get<{ secretSnapshots: TSecretSnapshot[] }>(
`/api/ee/v1/workspace/${workspaceId}/secret-snapshots`, `/api/v1/workspace/${workspaceId}/secret-snapshots`,
{ {
params: { params: {
limit, limit,
@@ -60,7 +60,7 @@ export const useGetWorkspaceSnapshotList = (dto: TGetSecretSnapshotsDTO & { isPa
const fetchSnapshotEncSecrets = async (snapshotId: string) => { const fetchSnapshotEncSecrets = async (snapshotId: string) => {
const res = await apiRequest.get<{ secretSnapshot: TSnapshotData }>( const res = await apiRequest.get<{ secretSnapshot: TSnapshotData }>(
`/api/ee/v1/secret-snapshot/${snapshotId}` `/api/v1/secret-snapshot/${snapshotId}`
); );
return res.data.secretSnapshot; return res.data.secretSnapshot;
}; };
@@ -142,7 +142,7 @@ const fetchWorkspaceSecretSnaphotCount = async (
directory = "/" directory = "/"
) => { ) => {
const res = await apiRequest.get<{ count: number }>( const res = await apiRequest.get<{ count: number }>(
`/api/ee/v1/workspace/${workspaceId}/secret-snapshots/count`, `/api/v1/workspace/${workspaceId}/secret-snapshots/count`,
{ {
params: { params: {
environment, environment,
@@ -170,7 +170,7 @@ export const usePerformSecretRollback = () => {
return useMutation<{}, {}, TSecretRollbackDTO>({ return useMutation<{}, {}, TSecretRollbackDTO>({
mutationFn: async ({ snapshotId }) => { 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; return data;
}, },
onSuccess: (_, { workspaceId, environment, directory }) => { onSuccess: (_, { workspaceId, environment, directory }) => {
+23 -31
View File
@@ -3,22 +3,20 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request"; import { apiRequest } from "@app/config/request";
const ssoConfigKeys = { 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) => { export const useGetSSOConfig = (organizationId: string) => {
return useQuery({ return useQuery({
queryKey: ssoConfigKeys.getSSOConfig(organizationId), queryKey: ssoConfigKeys.getSSOConfig(organizationId),
queryFn: async () => { queryFn: async () => {
const { data } = await apiRequest.get( const { data } = await apiRequest.get(`/api/v1/sso/config?organizationId=${organizationId}`);
`/api/v1/sso/config?organizationId=${organizationId}`
);
return data; return data;
}, },
enabled: true enabled: true
}); });
} };
export const useCreateSSOConfig = () => { export const useCreateSSOConfig = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
@@ -38,18 +36,15 @@ export const useCreateSSOConfig = () => {
issuer: string; issuer: string;
cert: string; cert: string;
}) => { }) => {
const { data } = await apiRequest.post( const { data } = await apiRequest.post("/api/v1/sso/config", {
"/api/v1/sso/config", organizationId,
{ authProvider,
organizationId, isActive,
authProvider, entryPoint,
isActive, issuer,
entryPoint, cert
issuer, });
cert
}
);
return data; return data;
}, },
onSuccess(_, dto) { onSuccess(_, dto) {
@@ -76,22 +71,19 @@ export const useUpdateSSOConfig = () => {
issuer?: string; issuer?: string;
cert?: string; cert?: string;
}) => { }) => {
const { data } = await apiRequest.patch( const { data } = await apiRequest.patch("/api/v1/sso/config", {
"/api/v1/sso/config", organizationId,
{ ...(authProvider !== undefined ? { authProvider } : {}),
organizationId, ...(isActive !== undefined ? { isActive } : {}),
...(authProvider !== undefined ? { authProvider } : {}), ...(entryPoint !== undefined ? { entryPoint } : {}),
...(isActive !== undefined ? { isActive } : {}), ...(issuer !== undefined ? { issuer } : {}),
...(entryPoint !== undefined ? { entryPoint } : {}), ...(cert !== undefined ? { cert } : {})
...(issuer !== undefined ? { issuer } : {}), });
...(cert !== undefined ? { cert } : {})
}
);
return data; return data;
}, },
onSuccess(_, dto) { onSuccess(_, dto) {
queryClient.invalidateQueries(ssoConfigKeys.getSSOConfig(dto.organizationId)); queryClient.invalidateQueries(ssoConfigKeys.getSSOConfig(dto.organizationId));
} }
}); });
}; };