Compare commits
29 Commits
docs-produ
...
log-github
Author | SHA1 | Date | |
---|---|---|---|
|
5a3aa3d608 | ||
|
95b327de50 | ||
|
a3c36f82f3 | ||
|
42612da57d | ||
|
f63c07d538 | ||
|
98a08d136e | ||
|
6c74b875f3 | ||
|
793cd4c144 | ||
|
ec0be1166f | ||
|
899d01237c | ||
|
ff5dbe74fd | ||
|
24004084f2 | ||
|
0e401ece73 | ||
|
c4e1651df7 | ||
|
514c7596db | ||
|
9fbdede82c | ||
|
e519637e89 | ||
|
ba393b0498 | ||
|
4150f81d83 | ||
|
a45bba8537 | ||
|
fe7e8e7240 | ||
|
cf54365022 | ||
|
4f26365c21 | ||
|
c974df104e | ||
|
e88fdc957e | ||
|
de2c1c5560 | ||
|
2cbd66e804 | ||
|
4a55ecbe12 | ||
|
1e29d550be |
189
backend/package-lock.json
generated
@@ -38,6 +38,7 @@
|
||||
"@octokit/core": "^5.2.1",
|
||||
"@octokit/plugin-paginate-graphql": "^4.0.1",
|
||||
"@octokit/plugin-retry": "^5.0.5",
|
||||
"@octokit/request": "8.4.1",
|
||||
"@octokit/rest": "^20.0.2",
|
||||
"@octokit/webhooks-types": "^7.3.1",
|
||||
"@octopusdeploy/api-client": "^3.4.1",
|
||||
@@ -9777,18 +9778,6 @@
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/auth-app/node_modules/@octokit/endpoint": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.1.tgz",
|
||||
"integrity": "sha512-JYjh5rMOwXMJyUpj028cu0Gbp7qe/ihxfJMLc8VZBMMqSwLgOxDI1911gV4Enl1QSavAQNJcwmwBF9M0VvLh6Q==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^13.0.0",
|
||||
"universal-user-agent": "^7.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/auth-app/node_modules/@octokit/openapi-types": {
|
||||
"version": "22.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz",
|
||||
@@ -9835,11 +9824,6 @@
|
||||
"node": "14 || >=16.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/auth-app/node_modules/universal-user-agent": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz",
|
||||
"integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q=="
|
||||
},
|
||||
"node_modules/@octokit/auth-oauth-app": {
|
||||
"version": "8.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-8.1.1.tgz",
|
||||
@@ -9855,18 +9839,6 @@
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/auth-oauth-app/node_modules/@octokit/endpoint": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.1.tgz",
|
||||
"integrity": "sha512-JYjh5rMOwXMJyUpj028cu0Gbp7qe/ihxfJMLc8VZBMMqSwLgOxDI1911gV4Enl1QSavAQNJcwmwBF9M0VvLh6Q==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^13.0.0",
|
||||
"universal-user-agent": "^7.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/auth-oauth-app/node_modules/@octokit/openapi-types": {
|
||||
"version": "22.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz",
|
||||
@@ -9905,11 +9877,6 @@
|
||||
"@octokit/openapi-types": "^22.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/auth-oauth-app/node_modules/universal-user-agent": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz",
|
||||
"integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q=="
|
||||
},
|
||||
"node_modules/@octokit/auth-oauth-device": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-7.1.1.tgz",
|
||||
@@ -9924,18 +9891,6 @@
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/auth-oauth-device/node_modules/@octokit/endpoint": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.1.tgz",
|
||||
"integrity": "sha512-JYjh5rMOwXMJyUpj028cu0Gbp7qe/ihxfJMLc8VZBMMqSwLgOxDI1911gV4Enl1QSavAQNJcwmwBF9M0VvLh6Q==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^13.0.0",
|
||||
"universal-user-agent": "^7.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/auth-oauth-device/node_modules/@octokit/openapi-types": {
|
||||
"version": "22.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz",
|
||||
@@ -9974,11 +9929,6 @@
|
||||
"@octokit/openapi-types": "^22.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/auth-oauth-device/node_modules/universal-user-agent": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz",
|
||||
"integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q=="
|
||||
},
|
||||
"node_modules/@octokit/auth-oauth-user": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-5.1.1.tgz",
|
||||
@@ -9994,18 +9944,6 @@
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/auth-oauth-user/node_modules/@octokit/endpoint": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.1.tgz",
|
||||
"integrity": "sha512-JYjh5rMOwXMJyUpj028cu0Gbp7qe/ihxfJMLc8VZBMMqSwLgOxDI1911gV4Enl1QSavAQNJcwmwBF9M0VvLh6Q==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^13.0.0",
|
||||
"universal-user-agent": "^7.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/auth-oauth-user/node_modules/@octokit/openapi-types": {
|
||||
"version": "22.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz",
|
||||
@@ -10044,11 +9982,6 @@
|
||||
"@octokit/openapi-types": "^22.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/auth-oauth-user/node_modules/universal-user-agent": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz",
|
||||
"integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q=="
|
||||
},
|
||||
"node_modules/@octokit/auth-token": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz",
|
||||
@@ -10102,32 +10035,38 @@
|
||||
"@octokit/openapi-types": "^24.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/core/node_modules/universal-user-agent": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
|
||||
"integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@octokit/endpoint": {
|
||||
"version": "9.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz",
|
||||
"integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==",
|
||||
"version": "10.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.4.tgz",
|
||||
"integrity": "sha512-OlYOlZIsfEVZm5HCSR8aSg02T2lbUWOsCQoPKfTXJwDzcHQBrVBGdGXb89dv2Kw2ToZaRtudp8O3ZIYoaOjKlA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^13.1.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
"@octokit/types": "^14.0.0",
|
||||
"universal-user-agent": "^7.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": {
|
||||
"version": "24.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
|
||||
"integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
|
||||
"version": "25.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz",
|
||||
"integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@octokit/endpoint/node_modules/@octokit/types": {
|
||||
"version": "13.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
|
||||
"integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
|
||||
"version": "14.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz",
|
||||
"integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^24.2.0"
|
||||
"@octokit/openapi-types": "^25.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/graphql": {
|
||||
@@ -10159,6 +10098,12 @@
|
||||
"@octokit/openapi-types": "^24.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/graphql/node_modules/universal-user-agent": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
|
||||
"integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@octokit/oauth-authorization-url": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-7.1.1.tgz",
|
||||
@@ -10181,18 +10126,6 @@
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/oauth-methods/node_modules/@octokit/endpoint": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.1.tgz",
|
||||
"integrity": "sha512-JYjh5rMOwXMJyUpj028cu0Gbp7qe/ihxfJMLc8VZBMMqSwLgOxDI1911gV4Enl1QSavAQNJcwmwBF9M0VvLh6Q==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^13.0.0",
|
||||
"universal-user-agent": "^7.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/oauth-methods/node_modules/@octokit/openapi-types": {
|
||||
"version": "22.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz",
|
||||
@@ -10231,11 +10164,6 @@
|
||||
"@octokit/openapi-types": "^22.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/oauth-methods/node_modules/universal-user-agent": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz",
|
||||
"integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q=="
|
||||
},
|
||||
"node_modules/@octokit/openapi-types": {
|
||||
"version": "19.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.1.0.tgz",
|
||||
@@ -10376,31 +10304,54 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": {
|
||||
"version": "22.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz",
|
||||
"integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg=="
|
||||
"version": "24.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
|
||||
"integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@octokit/request-error/node_modules/@octokit/types": {
|
||||
"version": "13.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.6.1.tgz",
|
||||
"integrity": "sha512-PHZE9Z+kWXb23Ndik8MKPirBPziOc0D2/3KH1P+6jK5nGWe96kadZuE4jev2/Jq7FvIfTlT2Ltg8Fv2x1v0a5g==",
|
||||
"version": "13.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
|
||||
"integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^22.2.0"
|
||||
"@octokit/openapi-types": "^24.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request/node_modules/@octokit/endpoint": {
|
||||
"version": "9.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz",
|
||||
"integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^13.1.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request/node_modules/@octokit/openapi-types": {
|
||||
"version": "22.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz",
|
||||
"integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg=="
|
||||
"version": "24.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
|
||||
"integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@octokit/request/node_modules/@octokit/types": {
|
||||
"version": "13.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.6.1.tgz",
|
||||
"integrity": "sha512-PHZE9Z+kWXb23Ndik8MKPirBPziOc0D2/3KH1P+6jK5nGWe96kadZuE4jev2/Jq7FvIfTlT2Ltg8Fv2x1v0a5g==",
|
||||
"version": "13.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
|
||||
"integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^22.2.0"
|
||||
"@octokit/openapi-types": "^24.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request/node_modules/universal-user-agent": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
|
||||
"integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@octokit/rest": {
|
||||
"version": "20.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.0.2.tgz",
|
||||
@@ -18288,7 +18239,8 @@
|
||||
"node_modules/fast-content-type-parse": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz",
|
||||
"integrity": "sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ=="
|
||||
"integrity": "sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-copy": {
|
||||
"version": "3.0.1",
|
||||
@@ -24776,6 +24728,12 @@
|
||||
"jsonwebtoken": "^9.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/octokit-auth-probot/node_modules/universal-user-agent": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
|
||||
"integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/odbc": {
|
||||
"version": "2.4.9",
|
||||
"resolved": "https://registry.npmjs.org/odbc/-/odbc-2.4.9.tgz",
|
||||
@@ -30705,9 +30663,10 @@
|
||||
"integrity": "sha512-G5o6f95b5BggDGuUfKDApKaCgNYy2x7OdHY0zSMF081O0EJobw+1130VONhrA7ezGSV2FNOGyM+KQpQZAr9bIQ=="
|
||||
},
|
||||
"node_modules/universal-user-agent": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
|
||||
"integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ=="
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz",
|
||||
"integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/universalify": {
|
||||
"version": "2.0.1",
|
||||
|
@@ -158,6 +158,7 @@
|
||||
"@octokit/core": "^5.2.1",
|
||||
"@octokit/plugin-paginate-graphql": "^4.0.1",
|
||||
"@octokit/plugin-retry": "^5.0.5",
|
||||
"@octokit/request": "8.4.1",
|
||||
"@octokit/rest": "^20.0.2",
|
||||
"@octokit/webhooks-types": "^7.3.1",
|
||||
"@octopusdeploy/api-client": "^3.4.1",
|
||||
|
@@ -0,0 +1,19 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
if (!(await knex.schema.hasColumn(TableName.Reminder, "fromDate"))) {
|
||||
await knex.schema.alterTable(TableName.Reminder, (t) => {
|
||||
t.timestamp("fromDate", { useTz: true }).nullable();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
if (await knex.schema.hasColumn(TableName.Reminder, "fromDate")) {
|
||||
await knex.schema.alterTable(TableName.Reminder, (t) => {
|
||||
t.dropColumn("fromDate");
|
||||
});
|
||||
}
|
||||
}
|
@@ -14,7 +14,8 @@ export const RemindersSchema = z.object({
|
||||
repeatDays: z.number().nullable().optional(),
|
||||
nextReminderDate: z.date(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date()
|
||||
updatedAt: z.date(),
|
||||
fromDate: z.date().nullable().optional()
|
||||
});
|
||||
|
||||
export type TReminders = z.infer<typeof RemindersSchema>;
|
||||
|
@@ -1,8 +1,10 @@
|
||||
// weird commonjs-related error in the CI requires us to do the import like this
|
||||
import knex from "knex";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import { TDbClient } from "@app/db";
|
||||
import { TableName, TAuditLogs } from "@app/db/schemas";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { DatabaseError, GatewayTimeoutError } from "@app/lib/errors";
|
||||
import { ormify, selectAllTableCols, TOrmify } from "@app/lib/knex";
|
||||
import { logger } from "@app/lib/logger";
|
||||
@@ -150,6 +152,7 @@ export const auditLogDALFactory = (db: TDbClient) => {
|
||||
|
||||
// delete all audit log that have expired
|
||||
const pruneAuditLog: TAuditLogDALFactory["pruneAuditLog"] = async (tx) => {
|
||||
const runPrune = async (dbClient: knex.Knex) => {
|
||||
const AUDIT_LOG_PRUNE_BATCH_SIZE = 10000;
|
||||
const MAX_RETRY_ON_FAILURE = 3;
|
||||
|
||||
@@ -161,7 +164,7 @@ export const auditLogDALFactory = (db: TDbClient) => {
|
||||
logger.info(`${QueueName.DailyResourceCleanUp}: audit log started`);
|
||||
do {
|
||||
try {
|
||||
const findExpiredLogSubQuery = (tx || db)(TableName.AuditLog)
|
||||
const findExpiredLogSubQuery = dbClient(TableName.AuditLog)
|
||||
.where("expiresAt", "<", today)
|
||||
.where("createdAt", "<", today) // to use audit log partition
|
||||
.orderBy(`${TableName.AuditLog}.createdAt`, "desc")
|
||||
@@ -169,7 +172,7 @@ export const auditLogDALFactory = (db: TDbClient) => {
|
||||
.limit(AUDIT_LOG_PRUNE_BATCH_SIZE);
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
deletedAuditLogIds = await (tx || db)(TableName.AuditLog)
|
||||
deletedAuditLogIds = await dbClient(TableName.AuditLog)
|
||||
.whereIn("id", findExpiredLogSubQuery)
|
||||
.del()
|
||||
.returning("id");
|
||||
@@ -188,5 +191,31 @@ export const auditLogDALFactory = (db: TDbClient) => {
|
||||
logger.info(`${QueueName.DailyResourceCleanUp}: audit log completed`);
|
||||
};
|
||||
|
||||
return { ...auditLogOrm, pruneAuditLog, find };
|
||||
if (tx) {
|
||||
await runPrune(tx);
|
||||
} else {
|
||||
const QUERY_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes
|
||||
await db.transaction(async (trx) => {
|
||||
await trx.raw(`SET statement_timeout = ${QUERY_TIMEOUT_MS}`);
|
||||
await runPrune(trx);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const create: TAuditLogDALFactory["create"] = async (tx) => {
|
||||
const config = getConfig();
|
||||
|
||||
if (config.DISABLE_AUDIT_LOG_STORAGE) {
|
||||
return {
|
||||
...tx,
|
||||
id: uuidv4(),
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date()
|
||||
};
|
||||
}
|
||||
|
||||
return auditLogOrm.create(tx);
|
||||
};
|
||||
|
||||
return { ...auditLogOrm, create, pruneAuditLog, find };
|
||||
};
|
||||
|
@@ -49,6 +49,7 @@ const baseSecretScanningDataSourceQuery = ({
|
||||
db.ref("encryptedCredentials").withSchema(TableName.AppConnection).as("connectionEncryptedCredentials"),
|
||||
db.ref("description").withSchema(TableName.AppConnection).as("connectionDescription"),
|
||||
db.ref("version").withSchema(TableName.AppConnection).as("connectionVersion"),
|
||||
db.ref("gatewayId").withSchema(TableName.AppConnection).as("connectionGatewayId"),
|
||||
db.ref("createdAt").withSchema(TableName.AppConnection).as("connectionCreatedAt"),
|
||||
db.ref("updatedAt").withSchema(TableName.AppConnection).as("connectionUpdatedAt"),
|
||||
db
|
||||
@@ -82,6 +83,7 @@ const expandSecretScanningDataSource = <
|
||||
connectionUpdatedAt,
|
||||
connectionVersion,
|
||||
connectionIsPlatformManagedCredentials,
|
||||
connectionGatewayId,
|
||||
...el
|
||||
} = dataSource;
|
||||
|
||||
@@ -100,7 +102,8 @@ const expandSecretScanningDataSource = <
|
||||
createdAt: connectionCreatedAt,
|
||||
updatedAt: connectionUpdatedAt,
|
||||
version: connectionVersion,
|
||||
isPlatformManagedCredentials: connectionIsPlatformManagedCredentials
|
||||
isPlatformManagedCredentials: connectionIsPlatformManagedCredentials,
|
||||
gatewayId: connectionGatewayId
|
||||
}
|
||||
: undefined
|
||||
};
|
||||
|
@@ -59,6 +59,7 @@ const envSchema = z
|
||||
AUDIT_LOGS_DB_ROOT_CERT: zpStr(
|
||||
z.string().describe("Postgres database base64-encoded CA cert for Audit logs").optional()
|
||||
),
|
||||
DISABLE_AUDIT_LOG_STORAGE: zodStrBool.default("false").optional().describe("Disable audit log storage"),
|
||||
MAX_LEASE_LIMIT: z.coerce.number().default(10000),
|
||||
DB_ROOT_CERT: zpStr(z.string().describe("Postgres database base64-encoded CA cert").optional()),
|
||||
DB_HOST: zpStr(z.string().describe("Postgres database host").optional()),
|
||||
@@ -482,6 +483,15 @@ export const overwriteSchema: {
|
||||
fields: { key: keyof TEnvConfig; description?: string }[];
|
||||
};
|
||||
} = {
|
||||
auditLogs: {
|
||||
name: "Audit Logs",
|
||||
fields: [
|
||||
{
|
||||
key: "DISABLE_AUDIT_LOG_STORAGE",
|
||||
description: "Disable audit log storage"
|
||||
}
|
||||
]
|
||||
},
|
||||
aws: {
|
||||
name: "AWS",
|
||||
fields: [
|
||||
|
@@ -2144,7 +2144,8 @@ export const registerRoutes = async (
|
||||
inviteOnlySignup: z.boolean().optional(),
|
||||
redisConfigured: z.boolean().optional(),
|
||||
secretScanningConfigured: z.boolean().optional(),
|
||||
samlDefaultOrgSlug: z.string().optional()
|
||||
samlDefaultOrgSlug: z.string().optional(),
|
||||
auditLogStorageDisabled: z.boolean().optional()
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -2171,7 +2172,8 @@ export const registerRoutes = async (
|
||||
inviteOnlySignup: Boolean(serverCfg.allowSignUp),
|
||||
redisConfigured: cfg.isRedisConfigured,
|
||||
secretScanningConfigured: cfg.isSecretScanningConfigured,
|
||||
samlDefaultOrgSlug: cfg.samlDefaultOrgSlug
|
||||
samlDefaultOrgSlug: cfg.samlDefaultOrgSlug,
|
||||
auditLogStorageDisabled: Boolean(cfg.DISABLE_AUDIT_LOG_STORAGE)
|
||||
};
|
||||
}
|
||||
});
|
||||
|
@@ -22,6 +22,7 @@ export const registerSecretReminderRouter = async (server: FastifyZodProvider) =
|
||||
message: z.string().trim().max(1024).optional(),
|
||||
repeatDays: z.number().min(1).nullable().optional(),
|
||||
nextReminderDate: z.string().datetime().nullable().optional(),
|
||||
fromDate: z.string().datetime().nullable().optional(),
|
||||
recipients: z.string().array().optional()
|
||||
})
|
||||
.refine((data) => {
|
||||
@@ -45,6 +46,7 @@ export const registerSecretReminderRouter = async (server: FastifyZodProvider) =
|
||||
message: req.body.message,
|
||||
repeatDays: req.body.repeatDays,
|
||||
nextReminderDate: req.body.nextReminderDate,
|
||||
fromDate: req.body.fromDate,
|
||||
recipients: req.body.recipients
|
||||
}
|
||||
});
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { createAppAuth } from "@octokit/auth-app";
|
||||
import { request } from "@octokit/request";
|
||||
import { AxiosError, AxiosRequestConfig, AxiosResponse } from "axios";
|
||||
import https from "https";
|
||||
import RE2 from "re2";
|
||||
@@ -12,7 +13,6 @@ import { GatewayProxyProtocol, withGatewayProxy } from "@app/lib/gateway";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator";
|
||||
import { getAppConnectionMethodName } from "@app/services/app-connection/app-connection-fns";
|
||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||
|
||||
import { AppConnection } from "../app-connection-enums";
|
||||
import { GitHubConnectionMethod } from "./github-connection-enums";
|
||||
@@ -30,6 +30,23 @@ export const getGitHubConnectionListItem = () => {
|
||||
};
|
||||
};
|
||||
|
||||
export const getGitHubInstanceApiUrl = async (config: {
|
||||
credentials: Pick<TGitHubConnectionConfig["credentials"], "host" | "instanceType">;
|
||||
}) => {
|
||||
const host = config.credentials.host || "github.com";
|
||||
|
||||
await blockLocalAndPrivateIpAddresses(host);
|
||||
|
||||
let apiBase: string;
|
||||
if (config.credentials.instanceType === "server") {
|
||||
apiBase = `${host}/api/v3`;
|
||||
} else {
|
||||
apiBase = `api.${host}`;
|
||||
}
|
||||
|
||||
return apiBase;
|
||||
};
|
||||
|
||||
export const requestWithGitHubGateway = async <T>(
|
||||
appConnection: { gatewayId?: string | null },
|
||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
||||
@@ -73,7 +90,10 @@ export const requestWithGitHubGateway = async <T>(
|
||||
return await httpRequest.request(finalRequestConfig);
|
||||
} catch (error) {
|
||||
const axiosError = error as AxiosError;
|
||||
logger.error("Error during GitHub gateway request:", axiosError.message, axiosError.response?.data);
|
||||
logger.error(
|
||||
{ message: axiosError.message, data: axiosError.response?.data },
|
||||
"Error during GitHub gateway request:"
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
@@ -112,7 +132,10 @@ export const getGitHubAppAuthToken = async (appConnection: TGitHubConnection) =>
|
||||
const appAuth = createAppAuth({
|
||||
appId,
|
||||
privateKey: appPrivateKey,
|
||||
installationId: appConnection.credentials.installationId
|
||||
installationId: appConnection.credentials.installationId,
|
||||
request: request.defaults({
|
||||
baseUrl: `https://${await getGitHubInstanceApiUrl(appConnection)}`
|
||||
})
|
||||
});
|
||||
|
||||
const { token } = await appAuth({ type: "installation" });
|
||||
@@ -141,7 +164,7 @@ export const makePaginatedGitHubRequest = async <T, R = T[]>(
|
||||
|
||||
const token =
|
||||
method === GitHubConnectionMethod.OAuth ? credentials.accessToken : await getGitHubAppAuthToken(appConnection);
|
||||
let url: string | null = `https://api.${credentials.host || "github.com"}${path}`;
|
||||
let url: string | null = `https://${await getGitHubInstanceApiUrl(appConnection)}${path}`;
|
||||
let results: T[] = [];
|
||||
let i = 0;
|
||||
|
||||
@@ -325,6 +348,8 @@ export const validateGitHubConnectionCredentials = async (
|
||||
});
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
logger.error(e, "Unable to verify GitHub connection");
|
||||
|
||||
if (e instanceof BadRequestError) {
|
||||
throw e;
|
||||
}
|
||||
@@ -355,7 +380,7 @@ export const validateGitHubConnectionCredentials = async (
|
||||
};
|
||||
}[];
|
||||
}>(config, gatewayService, {
|
||||
url: IntegrationUrls.GITHUB_USER_INSTALLATIONS.replace("api.github.com", `api.${host}`),
|
||||
url: `https://${await getGitHubInstanceApiUrl(config)}/user/installations`,
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
Authorization: `Bearer ${tokenResp.data.access_token}`,
|
||||
@@ -377,11 +402,15 @@ export const validateGitHubConnectionCredentials = async (
|
||||
switch (method) {
|
||||
case GitHubConnectionMethod.App:
|
||||
return {
|
||||
installationId: credentials.installationId
|
||||
installationId: credentials.installationId,
|
||||
instanceType: credentials.instanceType,
|
||||
host: credentials.host
|
||||
};
|
||||
case GitHubConnectionMethod.OAuth:
|
||||
return {
|
||||
accessToken: tokenResp.data.access_token
|
||||
accessToken: tokenResp.data.access_token,
|
||||
instanceType: credentials.instanceType,
|
||||
host: credentials.host
|
||||
};
|
||||
default:
|
||||
throw new InternalServerError({
|
||||
|
@@ -10,26 +10,59 @@ import {
|
||||
|
||||
import { GitHubConnectionMethod } from "./github-connection-enums";
|
||||
|
||||
export const GitHubConnectionOAuthInputCredentialsSchema = z.object({
|
||||
export const GitHubConnectionOAuthInputCredentialsSchema = z.union([
|
||||
z.object({
|
||||
code: z.string().trim().min(1, "OAuth code required"),
|
||||
instanceType: z.literal("server"),
|
||||
host: z.string().trim().min(1, "Host is required for server instance type")
|
||||
}),
|
||||
z.object({
|
||||
code: z.string().trim().min(1, "OAuth code required"),
|
||||
instanceType: z.literal("cloud").optional(),
|
||||
host: z.string().trim().optional()
|
||||
});
|
||||
})
|
||||
]);
|
||||
|
||||
export const GitHubConnectionAppInputCredentialsSchema = z.object({
|
||||
export const GitHubConnectionAppInputCredentialsSchema = z.union([
|
||||
z.object({
|
||||
code: z.string().trim().min(1, "GitHub App code required"),
|
||||
installationId: z.string().min(1, "GitHub App Installation ID required"),
|
||||
instanceType: z.literal("server"),
|
||||
host: z.string().trim().min(1, "Host is required for server instance type")
|
||||
}),
|
||||
z.object({
|
||||
code: z.string().trim().min(1, "GitHub App code required"),
|
||||
installationId: z.string().min(1, "GitHub App Installation ID required"),
|
||||
instanceType: z.literal("cloud").optional(),
|
||||
host: z.string().trim().optional()
|
||||
});
|
||||
})
|
||||
]);
|
||||
|
||||
export const GitHubConnectionOAuthOutputCredentialsSchema = z.object({
|
||||
export const GitHubConnectionOAuthOutputCredentialsSchema = z.union([
|
||||
z.object({
|
||||
accessToken: z.string(),
|
||||
instanceType: z.literal("server"),
|
||||
host: z.string().trim().min(1)
|
||||
}),
|
||||
z.object({
|
||||
accessToken: z.string(),
|
||||
instanceType: z.literal("cloud").optional(),
|
||||
host: z.string().trim().optional()
|
||||
});
|
||||
})
|
||||
]);
|
||||
|
||||
export const GitHubConnectionAppOutputCredentialsSchema = z.object({
|
||||
export const GitHubConnectionAppOutputCredentialsSchema = z.union([
|
||||
z.object({
|
||||
installationId: z.string(),
|
||||
instanceType: z.literal("server"),
|
||||
host: z.string().trim().min(1)
|
||||
}),
|
||||
z.object({
|
||||
installationId: z.string(),
|
||||
instanceType: z.literal("cloud").optional(),
|
||||
host: z.string().trim().optional()
|
||||
});
|
||||
})
|
||||
]);
|
||||
|
||||
export const ValidateGitHubConnectionCredentialsSchema = z.discriminatedUnion("method", [
|
||||
z.object({
|
||||
@@ -84,11 +117,17 @@ export const GitHubConnectionSchema = z.intersection(
|
||||
export const SanitizedGitHubConnectionSchema = z.discriminatedUnion("method", [
|
||||
BaseGitHubConnectionSchema.extend({
|
||||
method: z.literal(GitHubConnectionMethod.App),
|
||||
credentials: GitHubConnectionAppOutputCredentialsSchema.pick({})
|
||||
credentials: z.object({
|
||||
instanceType: z.union([z.literal("server"), z.literal("cloud")]).optional(),
|
||||
host: z.string().optional()
|
||||
})
|
||||
}),
|
||||
BaseGitHubConnectionSchema.extend({
|
||||
method: z.literal(GitHubConnectionMethod.OAuth),
|
||||
credentials: GitHubConnectionOAuthOutputCredentialsSchema.pick({})
|
||||
credentials: z.object({
|
||||
instanceType: z.union([z.literal("server"), z.literal("cloud")]).optional(),
|
||||
host: z.string().optional()
|
||||
})
|
||||
})
|
||||
]);
|
||||
|
||||
|
@@ -15,10 +15,15 @@ export const validateAltNameField = z
|
||||
.trim()
|
||||
.refine(
|
||||
(name) => {
|
||||
return isFQDN(name, { allow_wildcard: true }) || z.string().email().safeParse(name).success || isValidIp(name);
|
||||
return (
|
||||
isFQDN(name, { allow_wildcard: true, require_tld: false }) ||
|
||||
z.string().url().safeParse(name).success ||
|
||||
z.string().email().safeParse(name).success ||
|
||||
isValidIp(name)
|
||||
);
|
||||
},
|
||||
{
|
||||
message: "SAN must be a valid hostname, email address, or IP address"
|
||||
message: "SAN must be a valid hostname, email address, IP address or URL"
|
||||
}
|
||||
);
|
||||
|
||||
@@ -39,10 +44,15 @@ export const validateAltNamesField = z
|
||||
if (data === "") return true;
|
||||
// Split and validate each alt name
|
||||
return data.split(", ").every((name) => {
|
||||
return isFQDN(name, { allow_wildcard: true }) || z.string().email().safeParse(name).success || isValidIp(name);
|
||||
return (
|
||||
isFQDN(name, { allow_wildcard: true, require_tld: false }) ||
|
||||
z.string().url().safeParse(name).success ||
|
||||
z.string().email().safeParse(name).success ||
|
||||
isValidIp(name)
|
||||
);
|
||||
});
|
||||
},
|
||||
{
|
||||
message: "Each alt name must be a valid hostname or email address"
|
||||
message: "Each alt name must be a valid hostname, email address, IP address or URL"
|
||||
}
|
||||
);
|
||||
|
@@ -152,7 +152,7 @@ export const InternalCertificateAuthorityFns = ({
|
||||
extensions.push(extendedKeyUsagesExtension);
|
||||
}
|
||||
|
||||
let altNamesArray: { type: "email" | "dns"; value: string }[] = [];
|
||||
let altNamesArray: { type: "email" | "dns" | "ip" | "url"; value: string }[] = [];
|
||||
|
||||
if (subscriber.subjectAlternativeNames?.length) {
|
||||
altNamesArray = subscriber.subjectAlternativeNames.map((altName) => {
|
||||
@@ -160,10 +160,18 @@ export const InternalCertificateAuthorityFns = ({
|
||||
return { type: "email", value: altName };
|
||||
}
|
||||
|
||||
if (isFQDN(altName, { allow_wildcard: true })) {
|
||||
if (isFQDN(altName, { allow_wildcard: true, require_tld: false })) {
|
||||
return { type: "dns", value: altName };
|
||||
}
|
||||
|
||||
if (z.string().url().safeParse(altName).success) {
|
||||
return { type: "url", value: altName };
|
||||
}
|
||||
|
||||
if (z.string().ip().safeParse(altName).success) {
|
||||
return { type: "ip", value: altName };
|
||||
}
|
||||
|
||||
throw new BadRequestError({ message: `Invalid SAN entry: ${altName}` });
|
||||
});
|
||||
|
||||
@@ -418,7 +426,7 @@ export const InternalCertificateAuthorityFns = ({
|
||||
);
|
||||
}
|
||||
|
||||
let altNamesArray: { type: "email" | "dns"; value: string }[] = [];
|
||||
let altNamesArray: { type: "email" | "dns" | "ip" | "url"; value: string }[] = [];
|
||||
|
||||
if (altNames) {
|
||||
altNamesArray = altNames.split(",").map((altName) => {
|
||||
@@ -426,10 +434,18 @@ export const InternalCertificateAuthorityFns = ({
|
||||
return { type: "email", value: altName };
|
||||
}
|
||||
|
||||
if (isFQDN(altName, { allow_wildcard: true })) {
|
||||
if (isFQDN(altName, { allow_wildcard: true, require_tld: false })) {
|
||||
return { type: "dns", value: altName };
|
||||
}
|
||||
|
||||
if (z.string().url().safeParse(altName).success) {
|
||||
return { type: "url", value: altName };
|
||||
}
|
||||
|
||||
if (z.string().ip().safeParse(altName).success) {
|
||||
return { type: "ip", value: altName };
|
||||
}
|
||||
|
||||
throw new BadRequestError({ message: `Invalid SAN entry: ${altName}` });
|
||||
});
|
||||
|
||||
|
@@ -79,26 +79,34 @@ export const reminderServiceFactory = ({
|
||||
repeatDays,
|
||||
nextReminderDate: nextReminderDateInput,
|
||||
recipients,
|
||||
projectId
|
||||
projectId,
|
||||
fromDate: fromDateInput
|
||||
}: {
|
||||
secretId?: string;
|
||||
message?: string | null;
|
||||
repeatDays?: number | null;
|
||||
nextReminderDate?: string | null;
|
||||
recipients?: string[] | null;
|
||||
fromDate?: string | null;
|
||||
projectId: string;
|
||||
}) => {
|
||||
if (!secretId) {
|
||||
throw new BadRequestError({ message: "secretId is required" });
|
||||
}
|
||||
let nextReminderDate;
|
||||
let fromDate;
|
||||
if (nextReminderDateInput) {
|
||||
nextReminderDate = new Date(nextReminderDateInput);
|
||||
}
|
||||
|
||||
if (repeatDays && repeatDays > 0) {
|
||||
if (repeatDays) {
|
||||
if (fromDateInput) {
|
||||
fromDate = new Date(fromDateInput);
|
||||
nextReminderDate = fromDate;
|
||||
} else {
|
||||
nextReminderDate = $addDays(repeatDays);
|
||||
}
|
||||
}
|
||||
|
||||
if (!nextReminderDate) {
|
||||
throw new BadRequestError({ message: "repeatDays must be a positive number" });
|
||||
@@ -112,7 +120,8 @@ export const reminderServiceFactory = ({
|
||||
await reminderDAL.updateById(existingReminder.id, {
|
||||
message,
|
||||
repeatDays,
|
||||
nextReminderDate
|
||||
nextReminderDate,
|
||||
fromDate
|
||||
});
|
||||
reminderId = existingReminder.id;
|
||||
} else {
|
||||
@@ -121,7 +130,8 @@ export const reminderServiceFactory = ({
|
||||
secretId,
|
||||
message,
|
||||
repeatDays,
|
||||
nextReminderDate
|
||||
nextReminderDate,
|
||||
fromDate
|
||||
});
|
||||
reminderId = newReminder.id;
|
||||
}
|
||||
@@ -280,15 +290,29 @@ export const reminderServiceFactory = ({
|
||||
}
|
||||
|
||||
const processedReminders = remindersData.map(
|
||||
({ secretId, message, repeatDays, nextReminderDate: nextReminderDateInput, recipients, projectId }) => {
|
||||
({
|
||||
secretId,
|
||||
message,
|
||||
repeatDays,
|
||||
nextReminderDate: nextReminderDateInput,
|
||||
recipients,
|
||||
projectId,
|
||||
fromDate: fromDateInput
|
||||
}) => {
|
||||
let nextReminderDate;
|
||||
let fromDate;
|
||||
if (nextReminderDateInput) {
|
||||
nextReminderDate = new Date(nextReminderDateInput);
|
||||
}
|
||||
|
||||
if (repeatDays && repeatDays > 0 && !nextReminderDate) {
|
||||
if (repeatDays && !nextReminderDate) {
|
||||
if (fromDateInput) {
|
||||
fromDate = new Date(fromDateInput);
|
||||
nextReminderDate = fromDate;
|
||||
} else {
|
||||
nextReminderDate = $addDays(repeatDays);
|
||||
}
|
||||
}
|
||||
|
||||
if (!nextReminderDate) {
|
||||
throw new BadRequestError({
|
||||
@@ -302,17 +326,19 @@ export const reminderServiceFactory = ({
|
||||
repeatDays,
|
||||
nextReminderDate,
|
||||
recipients: recipients ? [...new Set(recipients)] : [],
|
||||
projectId
|
||||
projectId,
|
||||
fromDate
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const newReminders = await reminderDAL.insertMany(
|
||||
processedReminders.map(({ secretId, message, repeatDays, nextReminderDate }) => ({
|
||||
processedReminders.map(({ secretId, message, repeatDays, nextReminderDate, fromDate }) => ({
|
||||
secretId,
|
||||
message,
|
||||
repeatDays,
|
||||
nextReminderDate
|
||||
nextReminderDate,
|
||||
fromDate
|
||||
})),
|
||||
tx
|
||||
);
|
||||
|
@@ -8,6 +8,7 @@ export type TReminder = {
|
||||
message?: string | null;
|
||||
repeatDays?: number | null;
|
||||
nextReminderDate: Date;
|
||||
fromDate?: Date | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
@@ -21,6 +22,7 @@ export type TCreateReminderDTO = {
|
||||
secretId?: string;
|
||||
message?: string | null;
|
||||
repeatDays?: number | null;
|
||||
fromDate?: string | null;
|
||||
nextReminderDate?: string | null;
|
||||
recipients?: string[] | null;
|
||||
};
|
||||
@@ -31,6 +33,7 @@ export type TBatchCreateReminderDTO = {
|
||||
message?: string | null;
|
||||
repeatDays?: number | null;
|
||||
nextReminderDate?: string | Date | null;
|
||||
fromDate?: Date | null;
|
||||
recipients?: string[] | null;
|
||||
projectId?: string;
|
||||
}[];
|
||||
@@ -95,6 +98,7 @@ export interface TReminderServiceFactory {
|
||||
nextReminderDate?: string | null;
|
||||
recipients?: string[] | null;
|
||||
projectId: string;
|
||||
fromDate?: string | null;
|
||||
}) => Promise<{
|
||||
id: string;
|
||||
created: boolean;
|
||||
|
@@ -3,6 +3,7 @@ import sodium from "libsodium-wrappers";
|
||||
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
||||
import {
|
||||
getGitHubAppAuthToken,
|
||||
getGitHubInstanceApiUrl,
|
||||
GitHubConnectionMethod,
|
||||
makePaginatedGitHubRequest,
|
||||
requestWithGitHubGateway
|
||||
@@ -73,7 +74,7 @@ const getPublicKey = async (
|
||||
}
|
||||
|
||||
const response = await requestWithGitHubGateway<TGitHubPublicKey>(connection, gatewayService, {
|
||||
url: `https://api.${connection.credentials.host || "github.com"}${path}`,
|
||||
url: `https://${await getGitHubInstanceApiUrl(connection)}${path}`,
|
||||
method: "GET",
|
||||
headers: {
|
||||
Accept: "application/vnd.github+json",
|
||||
@@ -111,7 +112,7 @@ const deleteSecret = async (
|
||||
}
|
||||
|
||||
await requestWithGitHubGateway(connection, gatewayService, {
|
||||
url: `https://api.${connection.credentials.host || "github.com"}${path}`,
|
||||
url: `https://${await getGitHubInstanceApiUrl(connection)}${path}`,
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
Accept: "application/vnd.github+json",
|
||||
@@ -157,7 +158,7 @@ const putSecret = async (
|
||||
}
|
||||
|
||||
await requestWithGitHubGateway(connection, gatewayService, {
|
||||
url: `https://api.${connection.credentials.host || "github.com"}${path}`,
|
||||
url: `https://${await getGitHubInstanceApiUrl(connection)}${path}`,
|
||||
method: "PUT",
|
||||
headers: {
|
||||
Accept: "application/vnd.github+json",
|
||||
|
@@ -30,6 +30,7 @@ const baseSecretSyncQuery = ({ filter, db, tx }: { db: TDbClient; filter?: Secre
|
||||
db.ref("encryptedCredentials").withSchema(TableName.AppConnection).as("connectionEncryptedCredentials"),
|
||||
db.ref("description").withSchema(TableName.AppConnection).as("connectionDescription"),
|
||||
db.ref("version").withSchema(TableName.AppConnection).as("connectionVersion"),
|
||||
db.ref("gatewayId").withSchema(TableName.AppConnection).as("connectionGatewayId"),
|
||||
db.ref("createdAt").withSchema(TableName.AppConnection).as("connectionCreatedAt"),
|
||||
db.ref("updatedAt").withSchema(TableName.AppConnection).as("connectionUpdatedAt"),
|
||||
db
|
||||
@@ -65,6 +66,7 @@ const expandSecretSync = (
|
||||
connectionUpdatedAt,
|
||||
connectionVersion,
|
||||
connectionIsPlatformManagedCredentials,
|
||||
connectionGatewayId,
|
||||
...el
|
||||
} = secretSync;
|
||||
|
||||
@@ -83,7 +85,8 @@ const expandSecretSync = (
|
||||
createdAt: connectionCreatedAt,
|
||||
updatedAt: connectionUpdatedAt,
|
||||
version: connectionVersion,
|
||||
isPlatformManagedCredentials: connectionIsPlatformManagedCredentials
|
||||
isPlatformManagedCredentials: connectionIsPlatformManagedCredentials,
|
||||
gatewayId: connectionGatewayId
|
||||
},
|
||||
folder: folder
|
||||
? {
|
||||
|
@@ -1,7 +1,9 @@
|
||||
import { Button, Heading, Link, Section, Text } from "@react-email/components";
|
||||
import { Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseButton } from "./BaseButton";
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
import { BaseLink } from "./BaseLink";
|
||||
|
||||
interface AccessApprovalRequestTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
|
||||
projectName: string;
|
||||
@@ -38,18 +40,15 @@ export const AccessApprovalRequestTemplate = ({
|
||||
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
|
||||
You have a new access approval request pending review for the project <strong>{projectName}</strong>
|
||||
</Heading>
|
||||
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Section className="px-[24px] mb-[28px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
<strong>{requesterFullName}</strong> (
|
||||
<Link href={`mailto:${requesterEmail}`} className="text-slate-700 no-underline">
|
||||
{requesterEmail}
|
||||
</Link>
|
||||
) has requested {isTemporary ? "temporary" : "permanent"} access to <strong>{secretPath}</strong> in the{" "}
|
||||
<strong>{requesterFullName}</strong> (<BaseLink href={`mailto:${requesterEmail}`}>{requesterEmail}</BaseLink>)
|
||||
has requested {isTemporary ? "temporary" : "permanent"} access to <strong>{secretPath}</strong> in the{" "}
|
||||
<strong>{environment}</strong> environment.
|
||||
</Text>
|
||||
|
||||
{isTemporary && (
|
||||
<Text className="text-[14px] text-red-500 leading-[24px]">
|
||||
<Text className="text-[14px] text-red-600 leading-[24px]">
|
||||
<strong>This access will expire {expiresIn} after approval.</strong>
|
||||
</Text>
|
||||
)}
|
||||
@@ -67,13 +66,8 @@ export const AccessApprovalRequestTemplate = ({
|
||||
</Text>
|
||||
)}
|
||||
</Section>
|
||||
<Section className="text-center mt-[28px]">
|
||||
<Button
|
||||
href={approvalUrl}
|
||||
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
|
||||
>
|
||||
Review Request
|
||||
</Button>
|
||||
<Section className="text-center">
|
||||
<BaseButton href={approvalUrl}>Review Request</BaseButton>
|
||||
</Section>
|
||||
</BaseEmailWrapper>
|
||||
);
|
||||
|
18
backend/src/services/smtp/emails/BaseButton.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Button } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
type Props = {
|
||||
href: string;
|
||||
children: string;
|
||||
};
|
||||
|
||||
export const BaseButton = ({ href, children }: Props) => {
|
||||
return (
|
||||
<Button
|
||||
href={href}
|
||||
className="rounded-[8px] py-[12px] px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
|
||||
>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
};
|
@@ -16,23 +16,21 @@ export const BaseEmailWrapper = ({ title, preview, children, siteUrl }: BaseEmai
|
||||
<Body className="bg-gray-300 my-auto mx-auto font-sans px-[8px] py-[4px]">
|
||||
<Preview>{preview}</Preview>
|
||||
<Container className="bg-white rounded-xl my-[40px] mx-auto pb-[0px] max-w-[500px]">
|
||||
<Section className="border-0 border-b border-[#d1e309] border-solid bg-[#EBF852] mb-[44px] h-[10px] rounded-t-xl" />
|
||||
<Section className="px-[32px] mb-[18px]">
|
||||
<Section className="w-[48px] h-[48px] border border-solid border-gray-300 rounded-full bg-gray-100 mx-auto">
|
||||
<Section className="mb-[24px] px-[24px] mt-[24px]">
|
||||
<Img
|
||||
src={`https://infisical.com/_next/image?url=%2Fimages%2Flogo-black.png&w=64&q=75`}
|
||||
width="32"
|
||||
src="https://infisical.com/_next/image?url=%2Fimages%2Flogo-black.png&w=64&q=75"
|
||||
width="36"
|
||||
alt="Infisical Logo"
|
||||
className="mx-auto"
|
||||
/>
|
||||
</Section>
|
||||
</Section>
|
||||
<Hr className=" mb-[32px] mt-[0px] h-[1px]" />
|
||||
<Section className="px-[28px]">{children}</Section>
|
||||
<Hr className=" mt-[32px] mb-[0px] h-[1px]" />
|
||||
<Section className="px-[24px] text-center">
|
||||
<Text className="text-gray-500 text-[12px]">
|
||||
Email sent via{" "}
|
||||
<Link href={siteUrl} className="text-slate-700 no-underline">
|
||||
<Link href={siteUrl} className="text-slate-700 underline decoration-slate-700">
|
||||
Infisical
|
||||
</Link>
|
||||
</Text>
|
||||
|
15
backend/src/services/smtp/emails/BaseLink.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Link } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
type Props = {
|
||||
href: string;
|
||||
children: string;
|
||||
};
|
||||
|
||||
export const BaseLink = ({ href, children }: Props) => {
|
||||
return (
|
||||
<Link href={href} className="text-slate-700 underline decoration-slate-700">
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
};
|
@@ -1,7 +1,8 @@
|
||||
import { Heading, Link, Section, Text } from "@react-email/components";
|
||||
import { Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
import { BaseLink } from "./BaseLink";
|
||||
|
||||
interface EmailMfaTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
|
||||
code: string;
|
||||
@@ -25,11 +26,7 @@ export const EmailMfaTemplate = ({ code, siteUrl, isCloud }: EmailMfaTemplatePro
|
||||
<strong>Not you?</strong>{" "}
|
||||
{isCloud ? (
|
||||
<>
|
||||
Contact us at{" "}
|
||||
<Link href="mailto:support@infisical.com" className="text-slate-700 no-underline">
|
||||
support@infisical.com
|
||||
</Link>{" "}
|
||||
immediately
|
||||
Contact us at <BaseLink href="mailto:support@infisical.com">support@infisical.com</BaseLink> immediately
|
||||
</>
|
||||
) : (
|
||||
"Contact your administrator immediately"
|
||||
|
@@ -1,7 +1,8 @@
|
||||
import { Heading, Link, Section, Text } from "@react-email/components";
|
||||
import { Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
import { BaseLink } from "./BaseLink";
|
||||
|
||||
interface EmailVerificationTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
|
||||
code: string;
|
||||
@@ -29,10 +30,7 @@ export const EmailVerificationTemplate = ({ code, siteUrl, isCloud }: EmailVerif
|
||||
<strong>Questions about Infisical?</strong>{" "}
|
||||
{isCloud ? (
|
||||
<>
|
||||
Email us at{" "}
|
||||
<Link href="mailto:support@infisical.com" className="text-slate-700 no-underline">
|
||||
support@infisical.com
|
||||
</Link>
|
||||
Email us at <BaseLink href="mailto:support@infisical.com">support@infisical.com</BaseLink>
|
||||
</>
|
||||
) : (
|
||||
"Contact your administrator"
|
||||
|
@@ -1,7 +1,8 @@
|
||||
import { Heading, Link, Section, Text } from "@react-email/components";
|
||||
import { Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
import { BaseLink } from "./BaseLink";
|
||||
|
||||
interface ExternalImportFailedTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
|
||||
error: string;
|
||||
@@ -21,12 +22,9 @@ export const ExternalImportFailedTemplate = ({ error, siteUrl, provider }: Exter
|
||||
</Text>
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
If your issue persists, you can contact the Infisical team at{" "}
|
||||
<Link href="mailto:support@infisical.com" className="text-slate-700 no-underline">
|
||||
support@infisical.com
|
||||
</Link>
|
||||
.
|
||||
<BaseLink href="mailto:support@infisical.com">support@infisical.com</BaseLink>.
|
||||
</Text>
|
||||
<Text className="text-[14px] text-red-500 leading-[24px]">
|
||||
<Text className="text-[14px] text-red-600 leading-[24px]">
|
||||
<strong>Error:</strong> "{error}"
|
||||
</Text>
|
||||
</Section>
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { Button, Heading, Section, Text } from "@react-email/components";
|
||||
import { Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseButton } from "./BaseButton";
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
|
||||
interface IntegrationSyncFailedTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
|
||||
@@ -30,7 +31,7 @@ export const IntegrationSyncFailedTemplate = ({
|
||||
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
|
||||
<strong>{count}</strong> integration(s) failed to sync
|
||||
</Heading>
|
||||
<Section className="px-[24px] mt-[36px] pt-[26px] pb-[4px] text-[14px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Section className="px-[24px] mb-[28px] mt-[36px] pt-[26px] pb-[4px] text-[14px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<strong>Project</strong>
|
||||
<Text className="text-[14px] mt-[4px]">{projectName}</Text>
|
||||
<strong>Environment</strong>
|
||||
@@ -38,15 +39,10 @@ export const IntegrationSyncFailedTemplate = ({
|
||||
<strong>Secret Path</strong>
|
||||
<Text className="text-[14px] mt-[4px]">{secretPath}</Text>
|
||||
<strong className="text-black">Failure Reason:</strong>
|
||||
<Text className="text-[14px] mt-[4px] text-red-500 leading-[24px]">"{syncMessage}"</Text>
|
||||
<Text className="text-[14px] mt-[4px] text-red-600 leading-[24px]">"{syncMessage}"</Text>
|
||||
</Section>
|
||||
<Section className="text-center mt-[28px]">
|
||||
<Button
|
||||
href={integrationUrl}
|
||||
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
|
||||
>
|
||||
View Integrations
|
||||
</Button>
|
||||
<Section className="text-center">
|
||||
<BaseButton href={integrationUrl}>View Integrations</BaseButton>
|
||||
</Section>
|
||||
</BaseEmailWrapper>
|
||||
);
|
||||
|
@@ -1,7 +1,8 @@
|
||||
import { Heading, Link, Section, Text } from "@react-email/components";
|
||||
import { Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
import { BaseLink } from "./BaseLink";
|
||||
|
||||
interface NewDeviceLoginTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
|
||||
email: string;
|
||||
@@ -42,9 +43,7 @@ export const NewDeviceLoginTemplate = ({
|
||||
<Text className="mb-[0px]">
|
||||
If you believe that this login is suspicious, please contact{" "}
|
||||
{isCloud ? (
|
||||
<Link href="mailto:support@infisical.com" className="text-slate-700 no-underline">
|
||||
support@infisical.com
|
||||
</Link>
|
||||
<BaseLink href="mailto:support@infisical.com">support@infisical.com</BaseLink>
|
||||
) : (
|
||||
"your administrator"
|
||||
)}{" "}
|
||||
|
@@ -1,7 +1,8 @@
|
||||
import { Heading, Link, Section, Text } from "@react-email/components";
|
||||
import { Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
import { BaseLink } from "./BaseLink";
|
||||
|
||||
interface OrgAdminBreakglassAccessTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
|
||||
email: string;
|
||||
@@ -35,10 +36,7 @@ export const OrgAdminBreakglassAccessTemplate = ({
|
||||
<Text className="text-[14px] mt-[4px]">{userAgent}</Text>
|
||||
<Text className="text-[14px]">
|
||||
If you'd like to disable Admin SSO Bypass, please visit{" "}
|
||||
<Link href={`${siteUrl}/organization/settings`} className="text-slate-700 no-underline">
|
||||
Organization Security Settings
|
||||
</Link>
|
||||
.
|
||||
<BaseLink href={`${siteUrl}/organization/settings`}>Organization Security Settings</BaseLink>.
|
||||
</Text>
|
||||
</Section>
|
||||
</BaseEmailWrapper>
|
||||
|
@@ -2,6 +2,7 @@ import { Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
import { BaseLink } from "./BaseLink";
|
||||
|
||||
interface OrgAdminProjectGrantAccessTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview"> {
|
||||
email: string;
|
||||
@@ -24,8 +25,8 @@ export const OrgAdminProjectGrantAccessTemplate = ({
|
||||
</Heading>
|
||||
<Section className="px-[24px] mt-[36px] pt-[24px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Text className="text-[14px] mt-[4px]">
|
||||
The organization admin <strong>{email}</strong> has self-issued direct access to the project{" "}
|
||||
<strong>{projectName}</strong>.
|
||||
The organization admin <BaseLink href={`mailto:${email}`}>{email}</BaseLink> has self-issued direct access to
|
||||
the project <strong>{projectName}</strong>.
|
||||
</Text>
|
||||
</Section>
|
||||
</BaseEmailWrapper>
|
||||
|
@@ -1,7 +1,9 @@
|
||||
import { Button, Heading, Link, Section, Text } from "@react-email/components";
|
||||
import { Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseButton } from "./BaseButton";
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
import { BaseLink } from "./BaseLink";
|
||||
|
||||
interface OrganizationInvitationTemplateProps extends Omit<BaseEmailWrapperProps, "preview" | "title"> {
|
||||
metadata?: string;
|
||||
@@ -36,15 +38,13 @@ export const OrganizationInvitationTemplate = ({
|
||||
<br />
|
||||
<strong>{organizationName}</strong> on <strong>Infisical</strong>
|
||||
</Heading>
|
||||
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border text-center border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Section className="px-[24px] mb-[28px] mt-[36px] pt-[12px] pb-[8px] border text-center border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
{inviterFirstName && inviterUsername ? (
|
||||
<>
|
||||
<strong>{inviterFirstName}</strong> (
|
||||
<Link href={`mailto:${inviterUsername}`} className="text-slate-700 no-underline">
|
||||
{inviterUsername}
|
||||
</Link>
|
||||
) has invited you to collaborate on <strong>{organizationName}</strong>.
|
||||
<BaseLink href={`mailto:${inviterUsername}`}>{inviterUsername}</BaseLink>) has invited you to collaborate
|
||||
on <strong>{organizationName}</strong>.
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
@@ -53,13 +53,12 @@ export const OrganizationInvitationTemplate = ({
|
||||
)}
|
||||
</Text>
|
||||
</Section>
|
||||
<Section className="text-center mt-[28px]">
|
||||
<Button
|
||||
<Section className="text-center">
|
||||
<BaseButton
|
||||
href={`${callback_url}?token=${token}${metadata ? `&metadata=${metadata}` : ""}&to=${encodeURIComponent(email)}&organization_id=${organizationId}`}
|
||||
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
|
||||
>
|
||||
Accept Invite
|
||||
</Button>
|
||||
</BaseButton>
|
||||
</Section>
|
||||
<Section className="mt-[24px] bg-gray-50 pt-[2px] pb-[16px] border border-solid border-gray-200 px-[24px] rounded-md text-gray-800">
|
||||
<Text className="mb-[0px]">
|
||||
|
@@ -1,7 +1,9 @@
|
||||
import { Button, Heading, Link, Section, Text } from "@react-email/components";
|
||||
import { Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseButton } from "./BaseButton";
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
import { BaseLink } from "./BaseLink";
|
||||
|
||||
interface PasswordResetTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
|
||||
email: string;
|
||||
@@ -20,16 +22,13 @@ export const PasswordResetTemplate = ({ email, isCloud, siteUrl, callback_url, t
|
||||
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
|
||||
<strong>Account Recovery</strong>
|
||||
</Heading>
|
||||
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Section className="px-[24px] mb-[28px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Text className="text-[14px]">A password reset was requested for your Infisical account.</Text>
|
||||
<Text className="text-[14px]">
|
||||
If you did not initiate this request, please contact{" "}
|
||||
{isCloud ? (
|
||||
<>
|
||||
us immediately at{" "}
|
||||
<Link href="mailto:support@infisical.com" className="text-slate-700 no-underline">
|
||||
support@infisical.com
|
||||
</Link>
|
||||
us immediately at <BaseLink href="mailto:support@infisical.com">support@infisical.com</BaseLink>
|
||||
</>
|
||||
) : (
|
||||
"your administrator immediately"
|
||||
@@ -37,13 +36,8 @@ export const PasswordResetTemplate = ({ email, isCloud, siteUrl, callback_url, t
|
||||
.
|
||||
</Text>
|
||||
</Section>
|
||||
<Section className="text-center mt-[28px]">
|
||||
<Button
|
||||
href={`${callback_url}?token=${token}&to=${encodeURIComponent(email)}`}
|
||||
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
|
||||
>
|
||||
Reset Password
|
||||
</Button>
|
||||
<Section className="text-center">
|
||||
<BaseButton href={`${callback_url}?token=${token}&to=${encodeURIComponent(email)}`}>Reset Password</BaseButton>
|
||||
</Section>
|
||||
</BaseEmailWrapper>
|
||||
);
|
||||
|
@@ -1,6 +1,8 @@
|
||||
import { Button, Heading, Link, Section, Text } from "@react-email/components";
|
||||
import { Button, Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseLink } from "@app/services/smtp/emails/BaseLink";
|
||||
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
|
||||
interface PasswordSetupTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
|
||||
@@ -16,19 +18,16 @@ export const PasswordSetupTemplate = ({ email, isCloud, siteUrl, callback_url, t
|
||||
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
|
||||
<strong>Password Setup</strong>
|
||||
</Heading>
|
||||
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Section className="px-[24px] mb-[28px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Text className="text-[14px]">Someone requested to set up a password for your Infisical account.</Text>
|
||||
<Text className="text-[14px] text-red-500">
|
||||
<Text className="text-[14px] text-red-600">
|
||||
Make sure you are already logged in to Infisical in the current browser before clicking the link below.
|
||||
</Text>
|
||||
<Text className="text-[14px]">
|
||||
If you did not initiate this request, please contact{" "}
|
||||
{isCloud ? (
|
||||
<>
|
||||
us immediately at{" "}
|
||||
<Link href="mailto:support@infisical.com" className="text-slate-700 no-underline">
|
||||
support@infisical.com
|
||||
</Link>
|
||||
us immediately at <BaseLink href="mailto:support@infisical.com">support@infisical.com</BaseLink>
|
||||
</>
|
||||
) : (
|
||||
"your administrator immediately"
|
||||
@@ -36,7 +35,7 @@ export const PasswordSetupTemplate = ({ email, isCloud, siteUrl, callback_url, t
|
||||
.
|
||||
</Text>
|
||||
</Section>
|
||||
<Section className="text-center mt-[28px]">
|
||||
<Section className="text-center">
|
||||
<Button
|
||||
href={`${callback_url}?token=${token}&to=${encodeURIComponent(email)}`}
|
||||
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
|
||||
|
@@ -1,7 +1,9 @@
|
||||
import { Button, Heading, Link, Section, Text } from "@react-email/components";
|
||||
import { Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseButton } from "./BaseButton";
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
import { BaseLink } from "./BaseLink";
|
||||
|
||||
interface ProjectAccessRequestTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
|
||||
projectName: string;
|
||||
@@ -30,26 +32,17 @@ export const ProjectAccessRequestTemplate = ({
|
||||
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
|
||||
A user has requested access to the project <strong>{projectName}</strong>
|
||||
</Heading>
|
||||
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Section className="px-[24px] mb-[28px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
<strong>{requesterName}</strong> (
|
||||
<Link href={`mailto:${requesterEmail}`} className="text-slate-700 no-underline">
|
||||
{requesterEmail}
|
||||
</Link>
|
||||
) has requested access to the project <strong>{projectName}</strong> in the organization{" "}
|
||||
<strong>{orgName}</strong>.
|
||||
<strong>{requesterName}</strong> (<BaseLink href={`mailto:${requesterEmail}`}>{requesterEmail}</BaseLink>) has
|
||||
requested access to the project <strong>{projectName}</strong> in the organization <strong>{orgName}</strong>.
|
||||
</Text>
|
||||
<Text className="text-[14px] text-slate-700 leading-[24px]">
|
||||
<strong className="text-black">User note:</strong> "{note}"
|
||||
</Text>
|
||||
</Section>
|
||||
<Section className="text-center mt-[28px]">
|
||||
<Button
|
||||
href={callback_url}
|
||||
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
|
||||
>
|
||||
Grant Access
|
||||
</Button>
|
||||
<Section className="text-center">
|
||||
<BaseButton href={callback_url}>Grant Access</BaseButton>
|
||||
</Section>
|
||||
</BaseEmailWrapper>
|
||||
);
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { Button, Heading, Section, Text } from "@react-email/components";
|
||||
import { Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseButton } from "./BaseButton";
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
|
||||
interface ProjectInvitationTemplateProps extends Omit<BaseEmailWrapperProps, "preview" | "title"> {
|
||||
@@ -18,18 +19,13 @@ export const ProjectInvitationTemplate = ({ callback_url, workspaceName, siteUrl
|
||||
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
|
||||
You've been invited to join a project on Infisical
|
||||
</Heading>
|
||||
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border text-center border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Section className="px-[24px] mb-[28px] mt-[36px] pt-[12px] pb-[8px] border text-center border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
You've been invited to join the project <strong>{workspaceName}</strong>.
|
||||
</Text>
|
||||
</Section>
|
||||
<Section className="text-center mt-[28px]">
|
||||
<Button
|
||||
href={callback_url}
|
||||
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
|
||||
>
|
||||
Join Project
|
||||
</Button>
|
||||
<Section className="text-center">
|
||||
<BaseButton href={callback_url}>Join Project</BaseButton>
|
||||
</Section>
|
||||
<Section className="mt-[24px] bg-gray-50 pt-[2px] pb-[16px] border border-solid border-gray-200 px-[24px] rounded-md text-gray-800">
|
||||
<Text className="mb-[0px]">
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { Button, Heading, Section, Text } from "@react-email/components";
|
||||
import { Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseButton } from "./BaseButton";
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
|
||||
interface ScimUserProvisionedTemplateProps extends Omit<BaseEmailWrapperProps, "preview" | "title"> {
|
||||
@@ -24,18 +25,13 @@ export const ScimUserProvisionedTemplate = ({
|
||||
<br />
|
||||
<strong>{organizationName}</strong> on <strong>Infisical</strong>
|
||||
</Heading>
|
||||
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border text-center border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Section className="px-[24px] mb-[28px] mt-[36px] pt-[12px] pb-[8px] border text-center border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
You've been invited to collaborate on <strong>{organizationName}</strong>.
|
||||
</Text>
|
||||
</Section>
|
||||
<Section className="text-center mt-[28px]">
|
||||
<Button
|
||||
href={callback_url}
|
||||
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
|
||||
>
|
||||
Accept Invite
|
||||
</Button>
|
||||
<Section className="text-center">
|
||||
<BaseButton href={callback_url}>Accept Invite</BaseButton>
|
||||
</Section>
|
||||
<Section className="mt-[24px] bg-gray-50 pt-[2px] pb-[16px] border border-solid border-gray-200 px-[24px] rounded-md text-gray-800">
|
||||
<Text className="mb-[0px]">
|
||||
|
@@ -1,7 +1,9 @@
|
||||
import { Button, Heading, Link, Section, Text } from "@react-email/components";
|
||||
import { Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseButton } from "./BaseButton";
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
import { BaseLink } from "./BaseLink";
|
||||
|
||||
interface SecretApprovalRequestBypassedTemplateProps
|
||||
extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
|
||||
@@ -35,13 +37,10 @@ export const SecretApprovalRequestBypassedTemplate = ({
|
||||
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
|
||||
A secret approval request has been bypassed in the project <strong>{projectName}</strong>
|
||||
</Heading>
|
||||
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Section className="px-[24px] mb-[28px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
<strong>{requesterFullName}</strong> (
|
||||
<Link href={`mailto:${requesterEmail}`} className="text-slate-700 no-underline">
|
||||
{requesterEmail}
|
||||
</Link>
|
||||
) has {requestType === "change" ? "merged" : "accessed"} a secret {requestType === "change" ? "to" : "in"}{" "}
|
||||
<strong>{requesterFullName}</strong> (<BaseLink href={`mailto:${requesterEmail}`}>{requesterEmail}</BaseLink>)
|
||||
has {requestType === "change" ? "merged" : "accessed"} a secret {requestType === "change" ? "to" : "in"}{" "}
|
||||
<strong>{secretPath}</strong> in the <strong>{environment}</strong> environment without obtaining the required
|
||||
approval.
|
||||
</Text>
|
||||
@@ -50,13 +49,8 @@ export const SecretApprovalRequestBypassedTemplate = ({
|
||||
{bypassReason}"
|
||||
</Text>
|
||||
</Section>
|
||||
<Section className="text-center mt-[28px]">
|
||||
<Button
|
||||
href={approvalUrl}
|
||||
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
|
||||
>
|
||||
Review Bypass
|
||||
</Button>
|
||||
<Section className="text-center">
|
||||
<BaseButton href={approvalUrl}>Review Bypass</BaseButton>
|
||||
</Section>
|
||||
</BaseEmailWrapper>
|
||||
);
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { Button, Heading, Section, Text } from "@react-email/components";
|
||||
import { Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseButton } from "./BaseButton";
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
|
||||
interface SecretApprovalRequestNeedsReviewTemplateProps
|
||||
@@ -27,20 +28,15 @@ export const SecretApprovalRequestNeedsReviewTemplate = ({
|
||||
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
|
||||
A secret approval request for the project <strong>{projectName}</strong> requires review
|
||||
</Heading>
|
||||
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Section className="px-[24px] mb-[28px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Text className="text-[14px]">Hello {firstName},</Text>
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
You have a new secret change request pending your review for the project <strong>{projectName}</strong> in the
|
||||
organization <strong>{organizationName}</strong>.
|
||||
</Text>
|
||||
</Section>
|
||||
<Section className="text-center mt-[28px]">
|
||||
<Button
|
||||
href={approvalUrl}
|
||||
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
|
||||
>
|
||||
Review Changes
|
||||
</Button>
|
||||
<Section className="text-center">
|
||||
<BaseButton href={approvalUrl}>Review Changes</BaseButton>
|
||||
</Section>
|
||||
</BaseEmailWrapper>
|
||||
);
|
||||
|
@@ -1,7 +1,9 @@
|
||||
import { Button, Heading, Link, Section, Text } from "@react-email/components";
|
||||
import { Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseButton } from "./BaseButton";
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
import { BaseLink } from "./BaseLink";
|
||||
|
||||
interface SecretLeakIncidentTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
|
||||
numberOfSecrets: number;
|
||||
@@ -24,7 +26,7 @@ export const SecretLeakIncidentTemplate = ({
|
||||
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
|
||||
Infisical has uncovered <strong>{numberOfSecrets}</strong> secret(s) from a recent commit
|
||||
</Heading>
|
||||
<Section className="px-[24px] mt-[36px] pt-[8px] pb-[8px] text-[14px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Section className="px-[24px] mb-[28px] mt-[36px] pt-[8px] pb-[8px] text-[14px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Text className="text-[14px]">
|
||||
You are receiving this notification because one or more leaked secrets have been detected in a recent commit
|
||||
{(pusher_email || pusher_name) && (
|
||||
@@ -33,11 +35,7 @@ export const SecretLeakIncidentTemplate = ({
|
||||
pushed by <strong>{pusher_name ?? "Unknown Pusher"}</strong>{" "}
|
||||
{pusher_email && (
|
||||
<>
|
||||
(
|
||||
<Link href={`mailto:${pusher_email}`} className="text-slate-700 no-underline">
|
||||
{pusher_email}
|
||||
</Link>
|
||||
)
|
||||
(<BaseLink href={`mailto:${pusher_email}`}>{pusher_email}</BaseLink>)
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
@@ -49,24 +47,16 @@ export const SecretLeakIncidentTemplate = ({
|
||||
a comment in the given programming language. This will prevent future notifications from being sent out for
|
||||
these secrets.
|
||||
</Text>
|
||||
<Text className="text-[14px] text-red-500">
|
||||
<Text className="text-[14px] text-red-600">
|
||||
If these are production secrets, please rotate them immediately.
|
||||
</Text>
|
||||
<Text className="text-[14px]">
|
||||
Once you have taken action, be sure to update the status of the risk in the{" "}
|
||||
<Link href={`${siteUrl}/organization/secret-scanning`} className="text-slate-700 no-underline">
|
||||
Infisical Dashboard
|
||||
</Link>
|
||||
.
|
||||
<BaseLink href={`${siteUrl}/organization/secret-scanning`}>Infisical Dashboard</BaseLink>.
|
||||
</Text>
|
||||
</Section>
|
||||
<Section className="text-center mt-[28px]">
|
||||
<Button
|
||||
href={`${siteUrl}/organization/secret-scanning`}
|
||||
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
|
||||
>
|
||||
View Leaked Secrets
|
||||
</Button>
|
||||
<Section className="text-center">
|
||||
<BaseButton href={`${siteUrl}/organization/secret-scanning`}>View Leaked Secrets</BaseButton>
|
||||
</Section>
|
||||
</BaseEmailWrapper>
|
||||
);
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { Button, Heading, Section, Text } from "@react-email/components";
|
||||
import { Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseButton } from "./BaseButton";
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
|
||||
interface SecretRequestCompletedTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
|
||||
@@ -20,7 +21,7 @@ export const SecretRequestCompletedTemplate = ({
|
||||
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
|
||||
<strong>A secret has been shared with you</strong>
|
||||
</Heading>
|
||||
<Section className="px-[24px] mt-[36px] pt-[12px] text-center pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Section className="px-[24px] mb-[28px] mt-[36px] pt-[12px] text-center pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Text className="text-[14px]">
|
||||
{respondentUsername ? <strong>{respondentUsername}</strong> : "Someone"} shared a secret{" "}
|
||||
{name && (
|
||||
@@ -31,13 +32,8 @@ export const SecretRequestCompletedTemplate = ({
|
||||
with you.
|
||||
</Text>
|
||||
</Section>
|
||||
<Section className="text-center mt-[28px]">
|
||||
<Button
|
||||
href={secretRequestUrl}
|
||||
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
|
||||
>
|
||||
View Secret
|
||||
</Button>
|
||||
<Section className="text-center">
|
||||
<BaseButton href={secretRequestUrl}>View Secret</BaseButton>
|
||||
</Section>
|
||||
</BaseEmailWrapper>
|
||||
);
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { Button, Heading, Section, Text } from "@react-email/components";
|
||||
import { Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseButton } from "./BaseButton";
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
|
||||
interface SecretRotationFailedTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
|
||||
@@ -28,7 +29,7 @@ export const SecretRotationFailedTemplate = ({
|
||||
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
|
||||
Your <strong>{rotationType}</strong> rotation <strong>{rotationName}</strong> failed to rotate
|
||||
</Heading>
|
||||
<Section className="px-[24px] mt-[36px] pt-[26px] pb-[4px] text-[14px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Section className="px-[24px] mb-[28px] mt-[36px] pt-[26px] pb-[4px] text-[14px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<strong>Name</strong>
|
||||
<Text className="text-[14px] mt-[4px]">{rotationName}</Text>
|
||||
<strong>Type</strong>
|
||||
@@ -40,15 +41,12 @@ export const SecretRotationFailedTemplate = ({
|
||||
<strong>Secret Path</strong>
|
||||
<Text className="text-[14px] mt-[4px]">{secretPath}</Text>
|
||||
<strong>Reason:</strong>
|
||||
<Text className="text-[14px] text-red-500 mt-[4px]">{content}</Text>
|
||||
<Text className="text-[14px] text-red-600 mt-[4px]">{content}</Text>
|
||||
</Section>
|
||||
<Section className="text-center mt-[28px]">
|
||||
<Button
|
||||
href={`${rotationUrl}?search=${rotationName}&secretPath=${secretPath}`}
|
||||
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
|
||||
>
|
||||
<Section className="text-center">
|
||||
<BaseButton href={`${rotationUrl}?search=${rotationName}&secretPath=${secretPath}`}>
|
||||
View in Infisical
|
||||
</Button>
|
||||
</BaseButton>
|
||||
</Section>
|
||||
</BaseEmailWrapper>
|
||||
);
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { Button, Heading, Section, Text } from "@react-email/components";
|
||||
import { Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseButton } from "./BaseButton";
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
|
||||
interface SecretScanningScanFailedTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
|
||||
@@ -30,7 +31,7 @@ export const SecretScanningScanFailedTemplate = ({
|
||||
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
|
||||
Infisical encountered an error while attempting to scan the resource <strong>{resourceName}</strong>
|
||||
</Heading>
|
||||
<Section className="px-[24px] mt-[36px] pt-[26px] pb-[4px] text-[14px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Section className="px-[24px] mb-[28px] mt-[36px] pt-[26px] pb-[4px] text-[14px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<strong>Resource</strong>
|
||||
<Text className="text-[14px] mt-[4px]">{resourceName}</Text>
|
||||
<strong>Data Source</strong>
|
||||
@@ -40,15 +41,10 @@ export const SecretScanningScanFailedTemplate = ({
|
||||
<strong>Timestamp</strong>
|
||||
<Text className="text-[14px] mt-[4px]">{timestamp}</Text>
|
||||
<strong>Error</strong>
|
||||
<Text className="text-[14px] text-red-500 mt-[4px]">{errorMessage}</Text>
|
||||
<Text className="text-[14px] text-red-600 mt-[4px]">{errorMessage}</Text>
|
||||
</Section>
|
||||
<Section className="text-center mt-[28px]">
|
||||
<Button
|
||||
href={url}
|
||||
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
|
||||
>
|
||||
View in Infisical
|
||||
</Button>
|
||||
<Section className="text-center">
|
||||
<BaseButton href={url}>View in Infisical</BaseButton>
|
||||
</Section>
|
||||
</BaseEmailWrapper>
|
||||
);
|
||||
|
@@ -1,7 +1,9 @@
|
||||
import { Button, Heading, Link, Section, Text } from "@react-email/components";
|
||||
import { Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseButton } from "./BaseButton";
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
import { BaseLink } from "./BaseLink";
|
||||
|
||||
interface SecretScanningSecretsDetectedTemplateProps
|
||||
extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
|
||||
@@ -32,7 +34,7 @@ export const SecretScanningSecretsDetectedTemplate = ({
|
||||
Infisical has uncovered <strong>{numberOfSecrets}</strong> secret(s)
|
||||
{isDiffScan ? " from a recent commit to" : " in"} <strong>{resourceName}</strong>
|
||||
</Heading>
|
||||
<Section className="px-[24px] mt-[36px] pt-[8px] pb-[8px] text-[14px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Section className="px-[24px] mb-[28px] mt-[36px] pt-[8px] pb-[8px] text-[14px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Text className="text-[14px]">
|
||||
You are receiving this notification because one or more leaked secrets have been detected
|
||||
{isDiffScan && " in a recent commit"}
|
||||
@@ -43,11 +45,7 @@ export const SecretScanningSecretsDetectedTemplate = ({
|
||||
pushed by <strong>{authorName ?? "Unknown Pusher"}</strong>{" "}
|
||||
{authorEmail && (
|
||||
<>
|
||||
(
|
||||
<Link href={`mailto:${authorEmail}`} className="text-slate-700 no-underline">
|
||||
{authorEmail}
|
||||
</Link>
|
||||
)
|
||||
(<BaseLink href={`mailto:${authorEmail}`}>{authorEmail}</BaseLink>)
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
@@ -65,24 +63,16 @@ export const SecretScanningSecretsDetectedTemplate = ({
|
||||
a comment in the given programming language. This will prevent future notifications from being sent out for
|
||||
these secrets.
|
||||
</Text>
|
||||
<Text className="text-[14px] text-red-500">
|
||||
<Text className="text-[14px] text-red-600">
|
||||
If these are production secrets, please rotate them immediately.
|
||||
</Text>
|
||||
<Text className="text-[14px]">
|
||||
Once you have taken action, be sure to update the finding status in the{" "}
|
||||
<Link href={url} className="text-slate-700 no-underline">
|
||||
Infisical Dashboard
|
||||
</Link>
|
||||
.
|
||||
<BaseLink href={url}>Infisical Dashboard</BaseLink>.
|
||||
</Text>
|
||||
</Section>
|
||||
<Section className="text-center mt-[28px]">
|
||||
<Button
|
||||
href={url}
|
||||
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
|
||||
>
|
||||
View Leaked Secrets
|
||||
</Button>
|
||||
<Section className="text-center">
|
||||
<BaseButton href={url}>View Leaked Secrets</BaseButton>
|
||||
</Section>
|
||||
</BaseEmailWrapper>
|
||||
);
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { Button, Heading, Section, Text } from "@react-email/components";
|
||||
import { Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseButton } from "./BaseButton";
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
|
||||
interface SecretSyncFailedTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
|
||||
@@ -28,7 +29,7 @@ export const SecretSyncFailedTemplate = ({
|
||||
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
|
||||
Your <strong>{syncDestination}</strong> sync <strong>{syncName}</strong> failed to complete
|
||||
</Heading>
|
||||
<Section className="px-[24px] mt-[36px] pt-[26px] pb-[4px] text-[14px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Section className="px-[24px] mb-[28px] mt-[36px] pt-[26px] pb-[4px] text-[14px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<strong>Name</strong>
|
||||
<Text className="text-[14px] mt-[4px]">{syncName}</Text>
|
||||
<strong>Destination</strong>
|
||||
@@ -50,17 +51,12 @@ export const SecretSyncFailedTemplate = ({
|
||||
{failureMessage && (
|
||||
<>
|
||||
<strong>Reason:</strong>
|
||||
<Text className="text-[14px] text-red-500 mt-[4px]">{failureMessage}</Text>
|
||||
<Text className="text-[14px] text-red-600 mt-[4px]">{failureMessage}</Text>
|
||||
</>
|
||||
)}
|
||||
</Section>
|
||||
<Section className="text-center mt-[28px]">
|
||||
<Button
|
||||
href={syncUrl}
|
||||
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
|
||||
>
|
||||
View in Infisical
|
||||
</Button>
|
||||
<Section className="text-center">
|
||||
<BaseButton href={syncUrl}>View in Infisical</BaseButton>
|
||||
</Section>
|
||||
</BaseEmailWrapper>
|
||||
);
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { Button, Heading, Section, Text } from "@react-email/components";
|
||||
import { Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseButton } from "./BaseButton";
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
|
||||
interface ServiceTokenExpiryNoticeTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
|
||||
@@ -24,20 +25,15 @@ export const ServiceTokenExpiryNoticeTemplate = ({
|
||||
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
|
||||
<strong>Service token expiry notice</strong>
|
||||
</Heading>
|
||||
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Section className="px-[24px] mb-[28px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Text className="text-[14px]">
|
||||
Your service token <strong>{tokenName}</strong> for the project <strong>{projectName}</strong> will expire
|
||||
within 24 hours.
|
||||
</Text>
|
||||
<Text>If this token is still needed for your workflow, please create a new one before it expires.</Text>
|
||||
</Section>
|
||||
<Section className="text-center mt-[28px]">
|
||||
<Button
|
||||
href={url}
|
||||
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
|
||||
>
|
||||
Create New Token
|
||||
</Button>
|
||||
<Section className="text-center">
|
||||
<BaseButton href={url}>Create New Token</BaseButton>
|
||||
</Section>
|
||||
</BaseEmailWrapper>
|
||||
);
|
||||
|
@@ -1,7 +1,8 @@
|
||||
import { Heading, Link, Section, Text } from "@react-email/components";
|
||||
import { Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
import { BaseLink } from "./BaseLink";
|
||||
|
||||
interface SignupEmailVerificationTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
|
||||
code: string;
|
||||
@@ -29,10 +30,7 @@ export const SignupEmailVerificationTemplate = ({ code, siteUrl, isCloud }: Sign
|
||||
<strong>Questions about setting up Infisical?</strong>{" "}
|
||||
{isCloud ? (
|
||||
<>
|
||||
Email us at{" "}
|
||||
<Link href="mailto:support@infisical.com" className="text-slate-700 no-underline">
|
||||
support@infisical.com
|
||||
</Link>
|
||||
Email us at <BaseLink href="mailto:support@infisical.com">support@infisical.com</BaseLink>
|
||||
</>
|
||||
) : (
|
||||
"Contact your administrator"
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { Button, Heading, Section, Text } from "@react-email/components";
|
||||
import { Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseButton } from "./BaseButton";
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
|
||||
interface UnlockAccountTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
|
||||
@@ -18,19 +19,14 @@ export const UnlockAccountTemplate = ({ token, siteUrl, callback_url }: UnlockAc
|
||||
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
|
||||
<strong>Unlock your Infisical account</strong>
|
||||
</Heading>
|
||||
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Section className="px-[24px] mb-[28px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Text className="text-[14px]">
|
||||
Your account has been temporarily locked due to multiple failed login attempts.
|
||||
</Text>
|
||||
<Text>If these attempts were not made by you, reset your password immediately.</Text>
|
||||
</Section>
|
||||
<Section className="text-center mt-[28px]">
|
||||
<Button
|
||||
href={`${callback_url}?token=${token}`}
|
||||
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
|
||||
>
|
||||
Unlock Account
|
||||
</Button>
|
||||
<Section className="text-center">
|
||||
<BaseButton href={`${callback_url}?token=${token}`}>Unlock Account</BaseButton>
|
||||
</Section>
|
||||
</BaseEmailWrapper>
|
||||
);
|
||||
|
391
docs/docs.json
@@ -14,38 +14,84 @@
|
||||
"navigation": {
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Platform",
|
||||
"tab": "Documentation",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Getting Started",
|
||||
"pages": [
|
||||
"documentation/getting-started/overview",
|
||||
"documentation/getting-started/introduction",
|
||||
{
|
||||
"group": "Concepts",
|
||||
"pages": [
|
||||
"documentation/getting-started/concepts/deployment-models",
|
||||
"documentation/getting-started/concepts/platform-hierarchy",
|
||||
"documentation/getting-started/concepts/platform-iam",
|
||||
"documentation/getting-started/concepts/client-integrations",
|
||||
"documentation/getting-started/concepts/audit-logs"
|
||||
]
|
||||
"group": "Quickstart",
|
||||
"pages": ["documentation/guides/local-development"]
|
||||
},
|
||||
{
|
||||
"group": "Guides",
|
||||
"pages": ["documentation/guides/organization-structure"]
|
||||
"pages": [
|
||||
"documentation/guides/introduction",
|
||||
"documentation/guides/node",
|
||||
"documentation/guides/python",
|
||||
"documentation/guides/nextjs-vercel",
|
||||
"documentation/guides/microsoft-power-apps",
|
||||
"documentation/guides/organization-structure"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Setup",
|
||||
"pages": ["documentation/setup/networking"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Platform Reference",
|
||||
"group": "Platform",
|
||||
"pages": [
|
||||
"documentation/platform/organization",
|
||||
{
|
||||
"group": "Projects",
|
||||
"pages": [
|
||||
"documentation/platform/project",
|
||||
"documentation/platform/project-templates",
|
||||
"documentation/platform/folder",
|
||||
{
|
||||
"group": "Secrets",
|
||||
"pages": [
|
||||
"documentation/platform/secret-versioning",
|
||||
"documentation/platform/pit-recovery",
|
||||
"documentation/platform/secret-reference",
|
||||
"documentation/platform/webhooks"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Internal PKI",
|
||||
"pages": [
|
||||
"documentation/platform/pki/overview",
|
||||
"documentation/platform/pki/private-ca",
|
||||
"documentation/platform/pki/external-ca",
|
||||
"documentation/platform/pki/subscribers",
|
||||
"documentation/platform/pki/certificates",
|
||||
"documentation/platform/pki/acme-ca",
|
||||
"documentation/platform/pki/est",
|
||||
"documentation/platform/pki/alerting",
|
||||
{
|
||||
"group": "Integrations",
|
||||
"pages": [
|
||||
"documentation/platform/pki/pki-issuer",
|
||||
"documentation/platform/pki/integration-guides/gloo-mesh"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Infisical SSH",
|
||||
"pages": [
|
||||
"documentation/platform/ssh/overview",
|
||||
"documentation/platform/ssh/host-groups"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Key Management (KMS)",
|
||||
"pages": [
|
||||
"documentation/platform/kms/overview",
|
||||
"documentation/platform/kms/hsm-integration",
|
||||
"documentation/platform/kms/kubernetes-encryption",
|
||||
"documentation/platform/kms/kmip"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "KMS Configuration",
|
||||
"pages": [
|
||||
@@ -54,8 +100,6 @@
|
||||
"documentation/platform/kms-configuration/aws-hsm",
|
||||
"documentation/platform/kms-configuration/gcp-kms"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Identities",
|
||||
@@ -96,53 +140,57 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "App Connections",
|
||||
"group": "Secret Rotation",
|
||||
"pages": [
|
||||
"integrations/app-connections/overview",
|
||||
{
|
||||
"group": "Connections",
|
||||
"pages": [
|
||||
"integrations/app-connections/1password",
|
||||
"integrations/app-connections/auth0",
|
||||
"integrations/app-connections/aws",
|
||||
"integrations/app-connections/azure-app-configuration",
|
||||
"integrations/app-connections/azure-client-secrets",
|
||||
"integrations/app-connections/azure-devops",
|
||||
"integrations/app-connections/azure-key-vault",
|
||||
"integrations/app-connections/bitbucket",
|
||||
"integrations/app-connections/camunda",
|
||||
"integrations/app-connections/checkly",
|
||||
"integrations/app-connections/cloudflare",
|
||||
"integrations/app-connections/databricks",
|
||||
"integrations/app-connections/digital-ocean",
|
||||
"integrations/app-connections/flyio",
|
||||
"integrations/app-connections/gcp",
|
||||
"integrations/app-connections/github",
|
||||
"integrations/app-connections/github-radar",
|
||||
"integrations/app-connections/gitlab",
|
||||
"integrations/app-connections/hashicorp-vault",
|
||||
"integrations/app-connections/heroku",
|
||||
"integrations/app-connections/humanitec",
|
||||
"integrations/app-connections/ldap",
|
||||
"integrations/app-connections/mssql",
|
||||
"integrations/app-connections/mysql",
|
||||
"integrations/app-connections/netlify",
|
||||
"integrations/app-connections/oci",
|
||||
"integrations/app-connections/okta",
|
||||
"integrations/app-connections/oracledb",
|
||||
"integrations/app-connections/postgres",
|
||||
"integrations/app-connections/railway",
|
||||
"integrations/app-connections/render",
|
||||
"integrations/app-connections/supabase",
|
||||
"integrations/app-connections/teamcity",
|
||||
"integrations/app-connections/terraform-cloud",
|
||||
"integrations/app-connections/vercel",
|
||||
"integrations/app-connections/windmill",
|
||||
"integrations/app-connections/zabbix"
|
||||
]
|
||||
}
|
||||
"documentation/platform/secret-rotation/overview",
|
||||
"documentation/platform/secret-rotation/auth0-client-secret",
|
||||
"documentation/platform/secret-rotation/aws-iam-user-secret",
|
||||
"documentation/platform/secret-rotation/azure-client-secret",
|
||||
"documentation/platform/secret-rotation/ldap-password",
|
||||
"documentation/platform/secret-rotation/mssql-credentials",
|
||||
"documentation/platform/secret-rotation/mysql-credentials",
|
||||
"documentation/platform/secret-rotation/okta-client-secret",
|
||||
"documentation/platform/secret-rotation/oracledb-credentials",
|
||||
"documentation/platform/secret-rotation/postgres-credentials"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Dynamic Secrets",
|
||||
"pages": [
|
||||
"documentation/platform/dynamic-secrets/overview",
|
||||
"documentation/platform/dynamic-secrets/aws-elasticache",
|
||||
"documentation/platform/dynamic-secrets/aws-iam",
|
||||
"documentation/platform/dynamic-secrets/azure-entra-id",
|
||||
"documentation/platform/dynamic-secrets/cassandra",
|
||||
"documentation/platform/dynamic-secrets/elastic-search",
|
||||
"documentation/platform/dynamic-secrets/gcp-iam",
|
||||
"documentation/platform/dynamic-secrets/github",
|
||||
"documentation/platform/dynamic-secrets/ldap",
|
||||
"documentation/platform/dynamic-secrets/mongo-atlas",
|
||||
"documentation/platform/dynamic-secrets/mongo-db",
|
||||
"documentation/platform/dynamic-secrets/mssql",
|
||||
"documentation/platform/dynamic-secrets/mysql",
|
||||
"documentation/platform/dynamic-secrets/oracle",
|
||||
"documentation/platform/dynamic-secrets/postgresql",
|
||||
"documentation/platform/dynamic-secrets/rabbit-mq",
|
||||
"documentation/platform/dynamic-secrets/redis",
|
||||
"documentation/platform/dynamic-secrets/sap-ase",
|
||||
"documentation/platform/dynamic-secrets/sap-hana",
|
||||
"documentation/platform/dynamic-secrets/snowflake",
|
||||
"documentation/platform/dynamic-secrets/totp",
|
||||
"documentation/platform/dynamic-secrets/kubernetes",
|
||||
"documentation/platform/dynamic-secrets/vertica"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Gateway",
|
||||
"pages": [
|
||||
"documentation/platform/gateways/overview",
|
||||
"documentation/platform/gateways/gateway-security",
|
||||
"documentation/platform/gateways/networking"
|
||||
]
|
||||
},
|
||||
"documentation/platform/project-templates",
|
||||
{
|
||||
"group": "Workflow Integrations",
|
||||
"pages": [
|
||||
@@ -158,20 +206,22 @@
|
||||
"documentation/platform/external-migrations/vault"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Admin Consoles",
|
||||
"pages": [
|
||||
"documentation/platform/admin-panel/overview",
|
||||
"documentation/platform/admin-panel/server-admin",
|
||||
"documentation/platform/secret-sharing"
|
||||
"documentation/platform/admin-panel/org-admin-console"
|
||||
]
|
||||
},
|
||||
"documentation/platform/secret-sharing",
|
||||
{
|
||||
"group": "Connectivity",
|
||||
"group": "Secret Scanning",
|
||||
"pages": [
|
||||
"documentation/setup/networking",
|
||||
{
|
||||
"group": "Gateway",
|
||||
"pages": [
|
||||
"documentation/platform/gateways/overview",
|
||||
"documentation/platform/gateways/gateway-security",
|
||||
"documentation/platform/gateways/networking"
|
||||
"documentation/platform/secret-scanning/overview",
|
||||
"documentation/platform/secret-scanning/bitbucket",
|
||||
"documentation/platform/secret-scanning/github",
|
||||
"documentation/platform/secret-scanning/gitlab"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -376,80 +426,18 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "Products",
|
||||
"menu": [
|
||||
{
|
||||
"item": "Secrets Management",
|
||||
"tab": "Integrations",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Secrets Management",
|
||||
"pages": [
|
||||
"documentation/platform/secrets-mgmt/overview",
|
||||
"documentation/platform/secrets-mgmt/project",
|
||||
"documentation/platform/folder",
|
||||
{
|
||||
"group": "Secret Rotation",
|
||||
"pages": [
|
||||
"documentation/platform/secret-rotation/overview",
|
||||
"documentation/platform/secret-rotation/auth0-client-secret",
|
||||
"documentation/platform/secret-rotation/aws-iam-user-secret",
|
||||
"documentation/platform/secret-rotation/azure-client-secret",
|
||||
"documentation/platform/secret-rotation/ldap-password",
|
||||
"documentation/platform/secret-rotation/mssql-credentials",
|
||||
"documentation/platform/secret-rotation/mysql-credentials",
|
||||
"documentation/platform/secret-rotation/okta-client-secret",
|
||||
"documentation/platform/secret-rotation/oracledb-credentials",
|
||||
"documentation/platform/secret-rotation/postgres-credentials"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Dynamic Secrets",
|
||||
"pages": [
|
||||
"documentation/platform/dynamic-secrets/overview",
|
||||
"documentation/platform/dynamic-secrets/aws-elasticache",
|
||||
"documentation/platform/dynamic-secrets/aws-iam",
|
||||
"documentation/platform/dynamic-secrets/azure-entra-id",
|
||||
"documentation/platform/dynamic-secrets/cassandra",
|
||||
"documentation/platform/dynamic-secrets/elastic-search",
|
||||
"documentation/platform/dynamic-secrets/gcp-iam",
|
||||
"documentation/platform/dynamic-secrets/github",
|
||||
"documentation/platform/dynamic-secrets/ldap",
|
||||
"documentation/platform/dynamic-secrets/mongo-atlas",
|
||||
"documentation/platform/dynamic-secrets/mongo-db",
|
||||
"documentation/platform/dynamic-secrets/mssql",
|
||||
"documentation/platform/dynamic-secrets/mysql",
|
||||
"documentation/platform/dynamic-secrets/oracle",
|
||||
"documentation/platform/dynamic-secrets/postgresql",
|
||||
"documentation/platform/dynamic-secrets/rabbit-mq",
|
||||
"documentation/platform/dynamic-secrets/redis",
|
||||
"documentation/platform/dynamic-secrets/sap-ase",
|
||||
"documentation/platform/dynamic-secrets/sap-hana",
|
||||
"documentation/platform/dynamic-secrets/snowflake",
|
||||
"documentation/platform/dynamic-secrets/totp",
|
||||
"documentation/platform/dynamic-secrets/kubernetes",
|
||||
"documentation/platform/dynamic-secrets/vertica"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Guides",
|
||||
"pages": [
|
||||
"documentation/guides/introduction",
|
||||
"documentation/guides/local-development",
|
||||
"documentation/guides/node",
|
||||
"documentation/guides/python",
|
||||
"documentation/guides/nextjs-vercel",
|
||||
"documentation/guides/microsoft-power-apps"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Infrastructure Integrations",
|
||||
"pages": [
|
||||
"integrations/platforms/ansible",
|
||||
"integrations/platforms/apache-airflow",
|
||||
{
|
||||
"group": "Kubernetes Operator",
|
||||
"group": "Container orchestrators",
|
||||
"pages": [
|
||||
{
|
||||
"group": "Kubernetes",
|
||||
"pages": [
|
||||
"integrations/platforms/kubernetes/overview",
|
||||
"integrations/platforms/kubernetes/infisical-secret-crd",
|
||||
@@ -459,10 +447,6 @@
|
||||
},
|
||||
"integrations/platforms/kubernetes-injector",
|
||||
"integrations/platforms/kubernetes-csi",
|
||||
{
|
||||
"group": "Agent",
|
||||
"pages": [
|
||||
"integrations/platforms/infisical-agent",
|
||||
"integrations/platforms/docker-swarm-with-agent",
|
||||
"integrations/platforms/ecs-with-agent"
|
||||
]
|
||||
@@ -476,11 +460,60 @@
|
||||
"integrations/platforms/docker-compose"
|
||||
]
|
||||
},
|
||||
"integrations/platforms/infisical-agent",
|
||||
"integrations/frameworks/packer",
|
||||
"integrations/frameworks/pulumi",
|
||||
"integrations/frameworks/terraform"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "App Connections",
|
||||
"pages": [
|
||||
"integrations/app-connections/overview",
|
||||
{
|
||||
"group": "Connections",
|
||||
"pages": [
|
||||
"integrations/app-connections/1password",
|
||||
"integrations/app-connections/auth0",
|
||||
"integrations/app-connections/aws",
|
||||
"integrations/app-connections/azure-app-configuration",
|
||||
"integrations/app-connections/azure-client-secrets",
|
||||
"integrations/app-connections/azure-devops",
|
||||
"integrations/app-connections/azure-key-vault",
|
||||
"integrations/app-connections/bitbucket",
|
||||
"integrations/app-connections/camunda",
|
||||
"integrations/app-connections/checkly",
|
||||
"integrations/app-connections/cloudflare",
|
||||
"integrations/app-connections/databricks",
|
||||
"integrations/app-connections/digital-ocean",
|
||||
"integrations/app-connections/flyio",
|
||||
"integrations/app-connections/gcp",
|
||||
"integrations/app-connections/github",
|
||||
"integrations/app-connections/github-radar",
|
||||
"integrations/app-connections/gitlab",
|
||||
"integrations/app-connections/hashicorp-vault",
|
||||
"integrations/app-connections/heroku",
|
||||
"integrations/app-connections/humanitec",
|
||||
"integrations/app-connections/ldap",
|
||||
"integrations/app-connections/mssql",
|
||||
"integrations/app-connections/mysql",
|
||||
"integrations/app-connections/netlify",
|
||||
"integrations/app-connections/oci",
|
||||
"integrations/app-connections/okta",
|
||||
"integrations/app-connections/oracledb",
|
||||
"integrations/app-connections/postgres",
|
||||
"integrations/app-connections/railway",
|
||||
"integrations/app-connections/render",
|
||||
"integrations/app-connections/supabase",
|
||||
"integrations/app-connections/teamcity",
|
||||
"integrations/app-connections/terraform-cloud",
|
||||
"integrations/app-connections/vercel",
|
||||
"integrations/app-connections/windmill",
|
||||
"integrations/app-connections/zabbix"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Secret Syncs",
|
||||
"pages": [
|
||||
@@ -629,79 +662,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"item": "Secrets Scanning",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Secret Scanning",
|
||||
"pages": [
|
||||
"documentation/platform/secret-scanning/overview"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Datasources",
|
||||
"pages": [
|
||||
"documentation/platform/secret-scanning/bitbucket",
|
||||
"documentation/platform/secret-scanning/github",
|
||||
"documentation/platform/secret-scanning/gitlab"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"item": "Infisical PKI",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Infisical PKI",
|
||||
"pages": [
|
||||
"documentation/platform/pki/overview",
|
||||
"documentation/platform/pki/private-ca",
|
||||
"documentation/platform/pki/external-ca",
|
||||
"documentation/platform/pki/subscribers",
|
||||
"documentation/platform/pki/certificates",
|
||||
"documentation/platform/pki/acme-ca",
|
||||
"documentation/platform/pki/est",
|
||||
"documentation/platform/pki/alerting",
|
||||
{
|
||||
"group": "Integrations",
|
||||
"pages": [
|
||||
"documentation/platform/pki/pki-issuer",
|
||||
"documentation/platform/pki/integration-guides/gloo-mesh"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"item": "Infisical SSH",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Infisical SSH",
|
||||
"pages": [
|
||||
"documentation/platform/ssh/overview",
|
||||
"documentation/platform/ssh/host-groups"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"item": "Infisical KMS",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Infisical KMS",
|
||||
"pages": [
|
||||
"documentation/platform/kms/overview",
|
||||
"documentation/platform/kms/hsm-integration",
|
||||
"documentation/platform/kms/kubernetes-encryption",
|
||||
"documentation/platform/kms/kmip"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "CLI Reference",
|
||||
"tab": "CLI",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Command line",
|
||||
|
@@ -1,40 +0,0 @@
|
||||
---
|
||||
title: "Audit Logs"
|
||||
sidebarTitle: "Audit Logs"
|
||||
description: "Understand how Infisical logs activity and supports external audit streaming."
|
||||
---
|
||||
|
||||
Infisical records a detailed audit trail of actions across the platform — providing deep visibility into access, changes, and usage for security and compliance purposes.
|
||||
|
||||
Every interaction with Infisical resources generates an audit event. These events are immutable and include metadata such as the actor, event type, affected resources, timestamp, IP address, and client source.
|
||||
|
||||
Audit logs enable teams to:
|
||||
|
||||
- Monitor access and changes to secrets, certificates, and infrastructure.
|
||||
- Investigate incidents with full context around who did what, when, and how.
|
||||
- Meet compliance and governance requirements with structured activity records.
|
||||
|
||||
To learn more, refer to the [audit logs documentation](/documentation/platform/audit-logs).
|
||||
|
||||
## Log Coverage
|
||||
|
||||
Infisical tracks dozens of event types across the platform — including secret access, permission changes, certificate issuance, SSH session activity, and identity management.
|
||||
|
||||
Each audit entry includes structured fields that make it easy to search, filter, and correlate across systems. For example:
|
||||
|
||||
- Event Type: Action that occurred (e.g., `create-secret`, `issue-ssh-cert`).
|
||||
- Actor: Who performed the action (user or machine identity).
|
||||
- Resource: What was affected (e.g., project, secret, certificate).
|
||||
- Context: IP address, user agent, permissions, and more.
|
||||
|
||||
## External Log Streaming
|
||||
|
||||
For centralized monitoring and long-term retention, Infisical supports [audit log streaming](/documentation/platform/audit-log-streams/audit-log-streams) to external systems.
|
||||
|
||||
You can forward logs to SIEM platforms, storage buckets, or observability stacks using JSON-based collectors. Infisical integrates well with tools like [Fluent Bit](/documentation/platform/audit-log-streams/audit-log-streams-with-fluentbit#deploy-fluent-bit), enabling teams to route logs to destinations such as:
|
||||
|
||||
- AWS S3
|
||||
- Elasticsearch
|
||||
- Splunk
|
||||
- Datadog
|
||||
- Cloud-native log pipelines
|
@@ -1,31 +0,0 @@
|
||||
---
|
||||
title: "Client Ecosystem"
|
||||
sidebarTitle: "Client Ecosystem"
|
||||
description: "Get an overview of the CLI, SDKs, agents, APIs, and integrations that interact with Infisical."
|
||||
---
|
||||
|
||||
Infisical provides a flexible interface for integrating into development workflows and infrastructure. Around it is a rich ecosystem of clients and integrations that allow users and systems to interact with Infisical across any environment.
|
||||
|
||||
These clients enable access to secrets, certificates, and other resources from wherever they’re needed—whether that’s a developer’s terminal, a CI/CD pipeline, or a running Kubernetes workload.
|
||||
|
||||
## Available Clients and Interfaces
|
||||
|
||||
Infisical offers a non-exhaustive set of clients and interfaces to support a wide range of use cases:
|
||||
|
||||
- [CLI](/cli/overview): A powerful command-line interface for developers and operators to interact with Infisical from local or automated environments. Commonly used for secret access, SSH credential issuance, and more.
|
||||
|
||||
- [SDKs](/sdks/overview): Official client libraries for languages like Go, Node.js, and Python make it easy to integrate Infisical directly into applications and internal tooling.
|
||||
|
||||
- [HTTP API](/api-reference/overview/introduction): A fully documented RESTful API powers all core functionality and enables advanced or custom integrations.
|
||||
|
||||
- [Agents](/integrations/platforms/infisical-agent): Lightweight background processes that can fetch and sync secrets or credentials into local environments, containers, or file systems.
|
||||
|
||||
- [Kubernetes Operator](/integrations/platforms/kubernetes/overview): A native controller that syncs Infisical secrets into Kubernetes as native Secrets, and supports secure workload integration.
|
||||
|
||||
- [External Secrets Operator (ESO)](https://external-secrets.io/latest/provider/infisical): Allows Infisical to act as a backend provider for syncing secrets into Kubernetes `Secret` objects using the widely adopted External Secrets Operator.
|
||||
|
||||
- [Kubernetes PKI Issuer](/documentation/platform/pki/pki-issuer): A controller that issues X.509 certificates from Infisical PKI using the cert-manager Issuer and Certificate CRDs.
|
||||
|
||||
- [Secret Syncs](/integrations/secret-syncs/overview): Native integrations to forward secrets to services like GitHub, GitLab, AWS Secrets Manager, Vercel, and more.
|
||||
|
||||
This modular ecosystem lets teams use Infisical alongside their existing stack—without requiring opinionated workflows or lock-in.
|
@@ -1,52 +0,0 @@
|
||||
---
|
||||
title: "Using Infisical: Cloud or Self-Hosted"
|
||||
sidebarTitle: "Cloud vs. Self-Host"
|
||||
description: "Choose between Infisical Cloud or a self-managed deployment"
|
||||
---
|
||||
|
||||
Infisical can be used in two ways: via [Infisical Cloud](https://app.infisical.com), a managed offering, or through a self-hosted deployment within your own infrastructure.
|
||||
|
||||
Both options provide the same core platform capabilities. The decision depends on your operational model, trust boundaries, and compliance requirements. While Infisical Cloud comes with built-in security and operational guarantees, a self-hosted deployment gives you full control—but also full responsibility for securing and maintaining the system.
|
||||
|
||||
## Infisical Cloud
|
||||
|
||||
Infisical Cloud is our managed service found at [app.infisical.com](https://app.infisical.com). It includes automated updates, availability guarantees, and secure infrastructure operations.
|
||||
|
||||
For most teams, Infisical Cloud is the recommended way to get started. It simplifies adoption by removing the need to manage deployment, scaling, or maintenance internally.
|
||||
|
||||
Use this if:
|
||||
|
||||
- You prefer not to operate infrastructure or handle upgrades
|
||||
- You require a secure, production-grade hosted service
|
||||
- You want to adopt Infisical with minimal operational overhead
|
||||
|
||||
<Info>
|
||||
<p>
|
||||
By default, Infisical Cloud is a secure, multi-tenant service. For
|
||||
enterprises with stricter isolation or regulatory needs, dedicated cloud
|
||||
instances are available.
|
||||
</p>
|
||||
<p>Contact sales@infisical.com to learn more.</p>
|
||||
</Info>
|
||||
|
||||
## Self-Hosted Infisical
|
||||
|
||||
Infisical can also be deployed and managed within your own infrastructure. This approach provides full control over platform configuration, data storage, and operational security. In this model, your team is responsible for maintaining uptime, monitoring, patching, and integrations.
|
||||
|
||||
Use this if:
|
||||
|
||||
- You require complete control over data, deployment, and security posture
|
||||
- Your compliance model mandates self-managed or on-premise systems
|
||||
- You need to tightly integrate with internal tooling and infrastructure
|
||||
|
||||
Infisical supports multiple deployment methods, including [Docker](/self-hosting/deployment-options/standalone-infisical), [Docker Compose](/self-hosting/deployment-options/docker-compose), [Kubernetes](/self-hosting/deployment-options/kubernetes-helm), and [Linux package](/self-hosting/deployment-options/native/linux-package/installation).
|
||||
|
||||
To learn more, refer to the [self-hosting documentation](/self-hosting/overview).
|
||||
|
||||
<Info>
|
||||
<p>
|
||||
The open-source core is available under the MIT license. Additional
|
||||
enterprise features and support are available with a commercial license.
|
||||
</p>
|
||||
<p>Contact sales@infisical.com to learn more.</p>
|
||||
</Info>
|
@@ -1,41 +0,0 @@
|
||||
---
|
||||
title: "Platform Hierarchy"
|
||||
sidebarTitle: "Platform Hierarchy"
|
||||
description: "Understand how organizations and projects are structured in Infisical."
|
||||
---
|
||||
|
||||
Infisical is structured around organizations and projects, allowing teams to manage multiple products, access scopes, and use cases within a single account while keeping boundaries and responsibilities clearly defined.
|
||||
|
||||
## Organizations
|
||||
|
||||
An [organization](/documentation/platform/organization) typically represents a company or high-level entity (e.g. Acme Corp). It acts as the umbrella for all projects, members, and billing settings.
|
||||
|
||||
[Users](/documentation/platform/identities/user-identities) are invited to an organization and assigned [organization-level roles](/documentation/platform/access-controls/role-based-access-controls#organization-level-access-controls) that determine what they can manage—such as members, machine identities, and billing details.
|
||||
|
||||

|
||||
|
||||
## Projects
|
||||
|
||||
A [project](/documentation/platform/project) belongs to an organization and defines a specific scope of work. Each project has a product type such as Secrets Management, SSH, or PKI that determines what features are available in that project.
|
||||
|
||||
For example:
|
||||
|
||||
- A Secrets Management project manages application secrets across environments.
|
||||
|
||||
- An SSH project enables certificate-based access to infrastructure.
|
||||
|
||||
- A PKI project manages certificate authorities and X.509 certificate workflows.
|
||||
|
||||
Users are added to a project and assigned [project-level roles](/documentation/platform/access-controls/role-based-access-controls#project-level-access-controls) that determine what they can manage—such as secrets, access policies, or certificate authorities. A user can have different roles across projects, allowing for flexible and fine-grained access control that reflects how teams operate in practice.
|
||||
|
||||

|
||||
|
||||
## Key Characteristics
|
||||
|
||||
- Projects are isolated in terms of configuration, permissions, and product workflows.
|
||||
|
||||
- Access is managed independently at both the organization and project level.
|
||||
|
||||
- All projects within an organization share the same billing and user directory.
|
||||
|
||||
Teams can adopt Infisical incrementally—starting with one product and expanding as needed.
|
@@ -1,29 +0,0 @@
|
||||
---
|
||||
title: "Platform Identity and Access Management"
|
||||
sidebarTitle: "Platform IAM"
|
||||
description: "Understand how users, machine identities, roles, and permissions are managed."
|
||||
---
|
||||
|
||||
Infisical uses identity-based access control to govern how users and systems interact with secrets, certificates, infrastructure, and other resources on the platform.
|
||||
|
||||
There are two types of identities:
|
||||
|
||||
- [User identities](/documentation/platform/identities/user-identities): Represent individuals such as developers or administrators that typically access the platform via browser.
|
||||
|
||||
- [Machine identities](/documentation/platform/identities/machine-identities): Represent systems such as CI pipelines or applications that programmatically interact with the platform.
|
||||
|
||||
Each identity is granted access based on its assigned roles and permissions and must authenticate with the platform in order to access any resources.
|
||||
|
||||
To learn more, refer to the [identities documentation](/documentation/platform/identities/overview).
|
||||
|
||||
## Roles and Access
|
||||
|
||||
Infisical provides a robust and flexible access control system. The primary authorization mechanism is [role-based access control (RBAC)](/documentation/platform/access-controls/role-based-access-controls), where identities are assigned roles at two access control levels:
|
||||
|
||||
- [Organization-level access control](/documentation/platform/access-controls/role-based-access-controls#organization-level-access-controls): Control billing, member management, and platform-wide settings
|
||||
|
||||
- [Project-level access control](/documentation/platform/access-controls/role-based-access-controls#project-level-access-controls): Control access to specific product resources like secrets, SSH hosts, or certificates
|
||||
|
||||
Beyond RBAC, Infisical also supports additional project-level permissioning features, [including attribute-based access control (ABAC)](/documentation/platform/access-controls/abac/overview), [temporary access grants](/documentation/platform/access-controls/temporary-access), and [additional privileges](/documentation/platform/access-controls/additional-privileges) for select project types.
|
||||
|
||||
To learn more, refer to the [access control documentation](/documentation/platform/access-controls/overview).
|
107
docs/documentation/getting-started/introduction-new.mdx
Normal file
@@ -0,0 +1,107 @@
|
||||
---
|
||||
mode: 'custom'
|
||||
---
|
||||
|
||||
export function openSearch() {
|
||||
document.getElementById('search-bar-entry').click();
|
||||
}
|
||||
|
||||
<div
|
||||
className="relative w-full flex items-center justify-center"
|
||||
style={{ height: '24rem', backgroundColor: '#1F1F33', overflow: 'hidden' }}
|
||||
>
|
||||
<div style={{ flex: 'none' }}>
|
||||
<img
|
||||
src="/images/background.png"
|
||||
style={{ height: '68rem', width: '68rem' }}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ position: 'absolute', textAlign: 'center' }}>
|
||||
<div
|
||||
style={{
|
||||
color: 'white',
|
||||
fontWeight: '400',
|
||||
fontSize: '48px',
|
||||
margin: '0',
|
||||
}}
|
||||
>
|
||||
Infisical Documentation
|
||||
</div>
|
||||
<p
|
||||
style={{
|
||||
color: 'white',
|
||||
fontWeight: '400',
|
||||
fontSize: '20px',
|
||||
opacity: '0.7',
|
||||
}}
|
||||
>
|
||||
What can we help you build?
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
className="mx-auto w-full flex items-center text-sm leading-6 shadow-sm text-gray-400 bg-white ring-1 ring-gray-400/20 focus:outline-primary"
|
||||
id="home-search-entry"
|
||||
style={{
|
||||
maxWidth: '24rem',
|
||||
borderRadius: '4px',
|
||||
marginTop: '3rem',
|
||||
paddingLeft: '0.75rem',
|
||||
paddingRight: '0.75rem',
|
||||
paddingTop: '0.75rem',
|
||||
paddingBottom: '0.75rem',
|
||||
}}
|
||||
onClick={openSearch}
|
||||
>
|
||||
<svg
|
||||
className="h-4 w-4 ml-1.5 mr-3 flex-none bg-gray-500 hover:bg-gray-600 dark:bg-white/50 dark:hover:bg-white/70"
|
||||
style={{
|
||||
maskImage:
|
||||
'url("https://mintlify.b-cdn.net/v6.5.1/solid/magnifying-glass.svg")',
|
||||
maskRepeat: 'no-repeat',
|
||||
maskPosition: 'center center',
|
||||
}}
|
||||
/>
|
||||
Start a chat with us...
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{marginTop: '6rem', marginBottom: '8rem', maxWidth: '70rem', marginLeft: 'auto',
|
||||
marginRight: 'auto', paddingLeft: '1.25rem',
|
||||
paddingRight: '1.25rem' }}>
|
||||
|
||||
<div
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
fontSize: '24px',
|
||||
fontWeight: '600',
|
||||
color: '#121142',
|
||||
marginBottom: '3rem',
|
||||
}}
|
||||
>
|
||||
Choose a topic below or simply{' '}
|
||||
<span className="text-primary">get started</span>
|
||||
</div>
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="Getting Started" icon="book-open" href="/guides">
|
||||
Practical guides and best practices to get you up and running quickly.
|
||||
</Card>
|
||||
<Card title="API Reference" icon="code-simple" href="/reference">
|
||||
Comprehensive details about the Infisical API.
|
||||
</Card>
|
||||
<Card title="Security" icon="code-simple" href="/reference">
|
||||
Learn more about Infisical's architecture and underlying security.
|
||||
</Card>
|
||||
<Card title="Self-hosting" icon="link-simple" href="/integrations">
|
||||
Read self-hosting instruction for Infisical.
|
||||
</Card>
|
||||
<Card title="Integrations" icon="link-simple" href="/integrations">
|
||||
Infisical's growing number of third-party integrations.
|
||||
</Card>
|
||||
<Card title="Releases" icon="party-horn" href="/release-notes">
|
||||
News about features and changes in Pinecone and related tools.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
</div>
|
@@ -1,40 +1,106 @@
|
||||
---
|
||||
title: "What is Infisical?"
|
||||
sidebarTitle: "What is Infisical?"
|
||||
description: "The open source platform for managing secrets, certificates, and secure infrastructure access."
|
||||
description: "An Introduction to the Infisical secret management platform."
|
||||
---
|
||||
|
||||
## What is Infisical?
|
||||
|
||||
[Infisical](https://infisical.com) is the [open source](https://github.com/Infisical/infisical), all-in-one platform for secrets, certificates, and privileged access management.
|
||||
|
||||
It provides modern security workflows — including secrets rotation, dynamic credentials, access approvals, and SSH certificate-based access — all within one platform designed for developers, infrastructure, and security teams.
|
||||
**[Infisical](https://infisical.com)** is the open source secret management platform that developers use to centralize their application configuration and secrets like API keys and database credentials as well as manage their internal PKI. Additionally, developers use Infisical to prevent secrets leaks to git and securely share secrets amongst engineers.
|
||||
|
||||
Start managing secrets securely with [Infisical Cloud](https://app.infisical.com) or learn how to [host Infisical](/self-hosting/overview) yourself.
|
||||
|
||||
## Why use Infisical?
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Infisical Cloud"
|
||||
href="https://app.infisical.com/signup"
|
||||
icon="cloud"
|
||||
color="#000000"
|
||||
>
|
||||
Get started with Infisical Cloud in just a few minutes.
|
||||
</Card>
|
||||
<Card
|
||||
href="/self-hosting/overview"
|
||||
title="Self-hosting"
|
||||
icon="server"
|
||||
color="#000000"
|
||||
>
|
||||
Self-host Infisical on your own infrastructure.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
Managing secrets, credentials, and infrastructure access is a critical concern for engineering teams. As infrastructure scales and environments become more complex, [secrets start to sprawl](https://infisical.com/blog/what-is-secret-sprawl) — across codebases, CI/CD pipelines, configuration files, and cloud services. This makes them difficult to track, rotate, and secure.
|
||||
## Why Infisical?
|
||||
|
||||
Without proper management, secret sprawl turns into risk: hardcoded credentials, unrotated keys, fragmented access controls that attackers can exploit amongst other things.
|
||||
Infisical helps developers achieve secure centralized secret management and provides all the tools to easily manage secrets in various environments and infrastructure components. In particular, here are some of the most common points that developers mention after adopting Infisical:
|
||||
|
||||
Infisical addresses this challenge by providing an all-in-one platform and workflows to:
|
||||
- Streamlined **local development** processes (switching .env files to [Infisical CLI](/cli/commands/run) and removing secrets from developer machines).
|
||||
- **Best-in-class developer experience** with an easy-to-use [Web Dashboard](/documentation/platform/project).
|
||||
- Simple secret management inside **[CI/CD pipelines](/integrations/cicd/githubactions)** and staging environments.
|
||||
- Secure and compliant secret management practices in **[production environments](/sdks/overview)**.
|
||||
- **Facilitated workflows** around [secret change management](/documentation/platform/pr-workflows), [access requests](/documentation/platform/access-controls/access-requests), [temporary access provisioning](/documentation/platform/access-controls/temporary-access), and more.
|
||||
- **Improved security posture** thanks to [secret scanning](/cli/scanning-overview), [granular access control policies](/documentation/platform/access-controls/overview), [automated secret rotation](https://infisical.com/docs/documentation/platform/secret-rotation/overview), and [dynamic secrets](/documentation/platform/dynamic-secrets/overview) capabilities.
|
||||
|
||||
- Securely store and manage application secrets from development to production.
|
||||
- Scan code and pipelines for exposed credentials.
|
||||
- Automate X.509 certificate issuance and renewal.
|
||||
- Manage SSH access using short-lived, policy-driven certificates.
|
||||
- Encrypt and decrypt sensitive data with centralized key control.
|
||||
- Audit every access, credential use, and change.
|
||||
## How does Infisical work?
|
||||
|
||||
Infisical is designed to integrate cleanly into your stack—improving security without adding complexity.
|
||||
To make secret management effortless and secure, Infisical follows a certain structure for enabling secret management workflows as defined below.
|
||||
|
||||
## What does Infisical include?
|
||||
**Identities** in Infisical are users or machine which have a certain set of roles and permissions assigned to them. Such identities are able to manage secrets in various **Clients** throughout the entire infrastructure. To do that, identities have to verify themselves through one of the available **Authentication Methods**.
|
||||
|
||||
Infisical consists of several tightly integrated products, each designed to solve a specific part of the infrastructure security surface:
|
||||
As a result, the 3 main concepts that are important to understand are:
|
||||
|
||||
- [Secrets Management](/documentation/platform/secrets-mgmt/overview): Securely store, access, and distribute secrets across environments with fine-grained controls, automatic rotation, and audit logging.
|
||||
- [Secrets Scanning](/documentation/platform/secret-scanning/overview): Detect hardcoded secrets in code, CI pipelines, and infrastructure—integrated with GitHub, GitLab, Bitbucket, and more.
|
||||
- [Infisical PKI](/documentation/platform/pki/overview): Issue and manage X.509 certificates using protocols like EST, with support for internal and external CAs.
|
||||
- [Infisical SSH](/documentation/platform/ssh/overview): Provide short-lived SSH access to servers using certificate-based authentication, replacing static keys with policy-driven, time-bound control.
|
||||
- [Infisical KMS](/documentation/platform/kms/overview): Encrypt and decrypt data using centrally managed keys with enforced access policies and full audit visibility.
|
||||
- **[Identities](/documentation/platform/identities/overview)**: users or machines with a set permissions assigned to them.
|
||||
- **[Clients](/integrations/platforms/kubernetes)**: Infisical-developed tools for managing secrets in various infrastructure components (e.g., [Kubernetes Operator](/integrations/platforms/kubernetes), [Infisical Agent](/integrations/platforms/infisical-agent), [CLI](/cli/usage), [SDKs](/sdks/overview), [API](/api-reference/overview/introduction), [Web Dashboard](/documentation/platform/organization)).
|
||||
- **[Authentication Methods](/documentation/platform/identities/universal-auth)**: ways for Identities to authenticate inside different clients (e.g., SAML SSO for Web Dashboard, Universal Auth for Infisical Agent, AWS Auth etc.).
|
||||
|
||||
## How to get started with Infisical?
|
||||
|
||||
Depending on your use case, it might be helpful to look into some of the resources and guides provided below.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
href="../../cli/overview"
|
||||
title="Command Line Interface (CLI)"
|
||||
icon="square-terminal"
|
||||
color="#000000"
|
||||
>
|
||||
Inject secrets into any application process/environment.
|
||||
</Card>
|
||||
<Card
|
||||
title="SDKs"
|
||||
href="/documentation/getting-started/sdks"
|
||||
icon="boxes-stacked"
|
||||
color="#000000"
|
||||
>
|
||||
Fetch secrets with any programming language on demand.
|
||||
</Card>
|
||||
<Card
|
||||
href="../../integrations/platforms/docker-intro"
|
||||
title="Docker"
|
||||
icon="docker"
|
||||
color="#000000"
|
||||
>
|
||||
Inject secrets into Docker containers.
|
||||
</Card>
|
||||
<Card
|
||||
href="../../integrations/platforms/kubernetes"
|
||||
title="Kubernetes"
|
||||
icon="server"
|
||||
color="#000000"
|
||||
>
|
||||
Fetch and save secrets as native Kubernetes secrets.
|
||||
</Card>
|
||||
<Card
|
||||
href="/documentation/getting-started/api"
|
||||
title="REST API"
|
||||
icon="cloud"
|
||||
color="#000000"
|
||||
>
|
||||
Fetch secrets via HTTP request.
|
||||
</Card>
|
||||
<Card
|
||||
href="/integrations/overview"
|
||||
title="Native Integrations"
|
||||
icon="clouds"
|
||||
color="#000000"
|
||||
>
|
||||
Explore integrations for GitHub, Vercel, AWS, and more.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
@@ -1,77 +0,0 @@
|
||||
---
|
||||
title: "Overview"
|
||||
sidebarTitle: "Overview"
|
||||
description: "The open source platform for managing secrets, certificates, and secure infrastructure access."
|
||||
---
|
||||
|
||||
<Card
|
||||
title="What is Infisical?"
|
||||
href="/documentation/getting-started/introduction"
|
||||
>
|
||||
Learn what Infisical is and how it can help you manage secrets, certificates,
|
||||
and secure access across your infrastructure.
|
||||
</Card>
|
||||
|
||||
## Products
|
||||
|
||||
<Columns cols="2">
|
||||
<Card
|
||||
title="Secrets Management"
|
||||
href="/documentation/platform/secrets-mgmt/overview"
|
||||
>
|
||||
Securely store, manage, and control access to sensitive application secrets across your environments.
|
||||
|
||||
</Card>
|
||||
<Card
|
||||
title="Secrets Scanning"
|
||||
href="/documentation/platform/secret-scanning/overview"
|
||||
>
|
||||
Automatically detect and alert on hardcoded secrets in source code, CI pipelines, and infrastructure.
|
||||
</Card>
|
||||
<Card
|
||||
title="Infisical PKI"
|
||||
href="/documentation/platform/pki/overview"
|
||||
>
|
||||
Automate the issuance and management of X.509 certificates across your infrastructure using modern protocols like EST.
|
||||
</Card>
|
||||
<Card
|
||||
title="Infisical SSH"
|
||||
href="/documentation/platform/ssh/overview"
|
||||
>
|
||||
Replace static SSH keys with short-lived SSH certificates to simplify access and improve security.
|
||||
</Card>
|
||||
</Columns>
|
||||
|
||||
<Columns cols="1">
|
||||
<Card
|
||||
title="Infisical KMS"
|
||||
href="/documentation/platform/kms/overview"
|
||||
>
|
||||
Encrypt and decrypt sensitive data using a centralized key management system.
|
||||
</Card>
|
||||
</Columns>
|
||||
|
||||
## Resources
|
||||
|
||||
<Columns cols="2">
|
||||
<Card
|
||||
title="CLI Reference"
|
||||
href="/cli/overview"
|
||||
>
|
||||
Explore Infisical’s command-line interface for managing secrets,
|
||||
certificates, and system operations via terminal.
|
||||
</Card>
|
||||
<Card
|
||||
title="API Reference"
|
||||
href="/api-reference/overview/introduction"
|
||||
>
|
||||
Browse Infisical’s API documentation to programmatically interact with
|
||||
secrets, access controls, and certificate workflows.
|
||||
</Card>
|
||||
</Columns>
|
||||
<Columns cols="1">
|
||||
<Card title="Self-Hosting" href="/self-hosting/overview">
|
||||
Learn how to deploy and operate Infisical on your own infrastructure with full
|
||||
control and data ownership.
|
||||
</Card>
|
||||
</Columns>
|
@@ -11,12 +11,11 @@ Users can be added manually (through Web UI) or programmatically (e.g., API) to
|
||||
|
||||
Upon being added to an organization and projects, users assume a certain set of roles and permissions that represents their identity.
|
||||
|
||||

|
||||

|
||||
|
||||
## Authentication methods
|
||||
|
||||
To interact with various resources in Infisical, users are able to utilize a number of authentication methods:
|
||||
|
||||
- **Email & Password**: the most common authentication method that is used for authentication into Web Dashboard and Infisical CLI. It is recommended to utilize [Multi-factor Authentication](/documentation/platform/mfa) in addition to it.
|
||||
- **SSO**: Infisical natively integrates with a number of SSO identity providers like [Google](/documentation/platform/sso/google), [GitHub](/documentation/platform/sso/github), and [GitLab](/documentation/platform/sso/gitlab).
|
||||
- **SAML SSO**: It is also possible to set up SAML SSO integration with identity providers like [Okta](/documentation/platform/sso/okta), [Microsoft Entra ID](/documentation/platform/sso/azure) (formerly known as Azure AD), [JumpCloud](/documentation/platform/sso/jumpcloud), [Google](/documentation/platform/sso/google-saml), and more.
|
||||
|
@@ -3,94 +3,74 @@ title: "Organizations"
|
||||
description: "Learn more and understand the concept of Infisical organizations."
|
||||
---
|
||||
|
||||
Infisical is structured around organizations and [projects](/documentation/platform/project).
|
||||
|
||||
## Organizations
|
||||
|
||||
An organization represents a company or high-level entity (e.g. Acme Corp) and acts as the root scope for managing members and machine identities, projects, usage and billing, global integrations and configuration (such as single sign-on, provisioning, etc), and more.
|
||||
|
||||
Within an organization, you can create any number of projects—each tied to a specific product type such as Secrets Management or PKI that determines the functionality available.
|
||||
|
||||

|
||||
An Infisical organization is a set of [projects](./project) that use the same billing. Organizations allow one or more users to control billing and project permissions for all of the projects belonging to the organization. Each project belongs to an organization.
|
||||
|
||||
## Projects
|
||||
|
||||
The _Projects_ tab shows a list of projects that you have access to.
|
||||
The **Projects** page is where you can view the projects that you have access to within your organization
|
||||
as well as create a new project.
|
||||
|
||||
If you're an organization admin, you also have the option to view _All Projects_—a complete view of every project within the organization, including those you are not currently a member of— and gain access to any project.
|
||||

|
||||
|
||||
Admins can gain access to any project in the organization by opening the options menu (⋮) next to a project and selecting Access. This will add you to the project as an admin and allow full visibility and control.
|
||||
## Settings
|
||||
|
||||

|
||||
The **Settings** page lets you manage information about your organization including:
|
||||
|
||||
## Roles and Access Control
|
||||
- **Name**: The name of your organization.
|
||||
- **Slug**: The slug of your organization.
|
||||
- **Default Organization Member Role**: The role assigned to users when joining your organization unless otherwise specified.
|
||||
- **Incident Contacts**: Emails that should be alerted if anything abnormal is detected within the organization.
|
||||
- **Enabled Products**: Products which are enabled for your organization. This setting strictly affects the sidebar UI; disabling a product does not disable its API or routes.
|
||||
|
||||
The _Access Control_ tab lets you view and manage roles and permissions for users, machine identities, and groups across your organization.
|
||||

|
||||
|
||||
Users are invited to an organization and assigned organization-level roles such as `Admin` or `Member`. You can also define [custom roles](/documentation/platform/access-controls/role-based-access-controls#creating-custom-roles) at the organization level to fit your permission model.
|
||||
- Security and Authentication: A set of setting to enforce or manage [SAML](/documentation/platform/sso/overview), [OIDC](/documentation/platform/sso/overview), [SCIM](/documentation/platform/scim/overview), [LDAP](/documentation/platform/ldap/overview), and other authentication configurations.
|
||||
|
||||

|
||||

|
||||
|
||||
Infisical supports [user identities](/documentation/platform/identities/user-identities) (representing people) and [machine identities](/documentation/platform/identities/machine-identities) (representing services, CI/CD pipelines, or agents). The same roles and permissions can be applied to either type of identity.
|
||||
<Tip>
|
||||
You can adjust the maximum time a user token will remain valid for your organization. After this period, users will be required to re-authenticate. This helps improve security by enforcing regular sign-ins.
|
||||
</Tip>
|
||||
|
||||
To manage access at scale, Infisical also supports [user groups](/documentation/platform/groups) — roles assigned to a group apply to all of its members automatically.
|
||||
## Access Control
|
||||
|
||||
Note that Infisical distinguishes between organization-level and project-level access control:
|
||||
The **Access Control** page is where you can manage identities (both people and machines) that are part of your organization.
|
||||
You can add or remove additional members as well as modify their permissions.
|
||||
|
||||
- [Organization-level access control](/documentation/platform/access-controls/role-based-access-controls#organization-level-access-controls): Roles and permissions governing access to organization-level resources and controls such as billing, member management, and identity provider configuration.
|
||||
- [Project-level access control](/documentation/platform/access-controls/role-based-access-controls#project-level-access-controls): Roles and permissions governing access to resources and workflows within a specific project (e.g., secrets, certificates, SSH hosts).
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||
To learn more about how permissions work in detail, refer to the [access control documentation](/documentation/platform/access-controls/overview).
|
||||
In the **Organization Roles** tab, you can edit current or create new custom roles for members within the organization.
|
||||
|
||||
<Info>
|
||||
Infisical provides immutable roles such as `admin` and `member` for free.
|
||||
Note that Role-Based Access Management (RBAC) is partly a paid feature.
|
||||
|
||||
Infisical provides immutable roles like `admin`, `member`, etc.
|
||||
at the organization and project level for free.
|
||||
|
||||
If you're using Infisical Cloud, the ability to create custom roles is available under the **Pro Tier**.
|
||||
|
||||
If you're self-hosting Infisical, then you should contact sales@infisical.com to purchase an enterprise license to use it.
|
||||
|
||||
</Info>
|
||||
|
||||

|
||||
|
||||
As you can see next, Infisical supports granular permissions that you can tailor to each role.
|
||||
If you need certain members to only be able to access billing details, for example, then you can
|
||||
assign them that permission only.
|
||||
|
||||

|
||||
|
||||
## Usage & Billing
|
||||
|
||||
The _Usage & Billing_ tab provides an overview of your organization's billing information and platform usage.
|
||||
The **Usage & Billing** page applies only to [Infisical Cloud](https://app.infisical.com) and is where you can
|
||||
manage your plan and billing information.
|
||||
|
||||
Infisical calculates usage at the organization level—aggregating activity across all projects and product types (e.g., Secrets Management, SSH, PKI). From this tab, you can track usage, view billing details, and manage your Infisical Cloud subscription.
|
||||
This includes the following items:
|
||||
|
||||

|
||||
- Current plan: The current plan information such as what tier your organization is on and what features/limits apply to this tier.
|
||||
- Licenses: The license keys for self-hosted instances of Infisical (if applicable).
|
||||
- Receipts: The receipts of monthly/annual invoices.
|
||||
- Billing: The billing details of your organization including payment methods on file, tax IDs (if applicable), etc.
|
||||
|
||||
## Audit Logs
|
||||
|
||||
Infisical provides a unified view of [audit logs](/documentation/platform/audit-logs) at the organization level. All platform activity—including secret access, certificate issuance, platform logins across the organization —is recorded and searchable in a central log view.
|
||||
|
||||
Audit logs are also viewable at the project level, where they are scoped to show only events relevant to that specific project. This allows project administrators to monitor activity and investigate changes without requiring organization-wide access.
|
||||
|
||||
## App Connections
|
||||
|
||||
Infisical supports [app connections](/integrations/app-connections/overview) — integrations configured at the organization level with third-party platforms such as AWS, GCP, GitHub, and many others.
|
||||
|
||||
Once configured, these connections can be reused across multiple projects as part of any feature that requires third-party integrations—such as [secret syncing](/integrations/secret-syncs/overview) or [dynamic credential generation](/documentation/platform/dynamic-secrets/overview).
|
||||
|
||||

|
||||
|
||||
To learn more, refer to the [app connections documentation](/integrations/app-connections/overview).
|
||||
|
||||
## Organization Settings
|
||||
|
||||
The _Organization Settings_ tab lets you configure global behavior and security controls for the organization.
|
||||
|
||||
Key configuration areas include:
|
||||
|
||||
- General: Manage the organization’s name, slug, and default role for newly invited members.
|
||||
- Single Sign-On (SSO): Enable [SAML](/documentation/platform/sso/overview), [LDAP](/documentation/platform/ldap/overview), or [OIDC-based](/documentation/platform/sso/general-oidc/overview) authentication for user login.
|
||||
- Provisioning: Enable [SCIM](/documentation/platform/scim/overview) to automatically provision and deprovision users and groups from an identity provider.
|
||||
- Security Policies: Enforce MFA and configure session duration limits.
|
||||
- Encryption: Integrate with external KMS systems or bring your own encryption keys (BYOK).
|
||||
- [Audit Log Streaming](/documentation/platform/audit-log-streams/audit-log-streams): Forward audit events to third-party logging tools like SIEMs or cloud storage.
|
||||
- Workflow Integrations: Trigger [Slack](/documentation/platform/workflow-integrations/slack-integration) or [Microsoft Teams](/documentation/platform/workflow-integrations/microsoft-teams-integration) notifications for events like access requests.
|
||||
- [Project Templates](/documentation/platform/project-templates): Define default environments, roles, and settings to standardize project creation.
|
||||
- KMIP (Enterprise): Connect to KMIP-compatible HSMs for hardware-backed key storage and operations.
|
||||
|
||||

|
||||

|
||||
|
@@ -1,13 +1,13 @@
|
||||
---
|
||||
title: "Infisical PKI"
|
||||
title: "Internal PKI"
|
||||
sidebarTitle: "Overview"
|
||||
description: "Learn how to create a Private CA hierarchy and issue X.509 certificates."
|
||||
---
|
||||
|
||||
Infisical can be used to create and manage Certificate Authorities (CAs) and issue X.509 certificates. This allows you to manage PKI infrastructure and issue digital certificates for subscribers such as services, applications, and devices.
|
||||
Infisical can be used to create a Private Certificate Authority (CA) hierarchy and issue X.509 certificates for internal use. This allows you to manage your own PKI infrastructure and issue digital certificates for subscribers such as services, applications, and devices.
|
||||
|
||||
Infisical's PKI offering is split into three components:
|
||||
|
||||
- [Certificate Authorities](/documentation/platform/pki/private-ca): Create and manage CAs, including root and intermediate CAs.
|
||||
- [Certificate Authorities](/documentation/platform/pki/private-ca): Create and manage private CAs, including root and intermediate CAs.
|
||||
- [Subscribers](/documentation/platform/pki/subscribers): Define and manage entities that will request X.509 certificates from CAs. This module provides a centralized view of all subscribers, enabling you to issue certificates and monitor their status.
|
||||
- [Certificates](/documentation/platform/pki/certificates): Track and monitor issued X.509 certificates, maintaining a comprehensive inventory of all active and expired certificates.
|
||||
|
@@ -1,51 +1,116 @@
|
||||
---
|
||||
title: "Overview"
|
||||
title: "Projects"
|
||||
description: "Learn more and understand the concept of Infisical projects."
|
||||
---
|
||||
|
||||
## Projects
|
||||
A project in Infisical belongs to an [organization](./organization) and contains a number of environments, folders, and secrets.
|
||||
Only users and machine identities who belong to a project can access resources inside of it according to predefined permissions.
|
||||
|
||||
A project defines a specific scope of work for a given product line in Infisical.
|
||||
Infisical also allows users to request project access. Refer to the [project access request section](./access-controls/project-access-requests)
|
||||
|
||||
Projects are created within an [organization](/documentation/platform/organization), and an organization can contain multiple projects across different product types.
|
||||
## Project environments
|
||||
|
||||
## Project Types
|
||||
For both visual and organizational structure, Infisical allows splitting up secrets into environments (e.g., development, staging, production). In project settings, such environments can be
|
||||
customized depending on the intended use case.
|
||||
|
||||
Infisical supports project types, each representing a different security product with its own dashboard, workflows, and capabilities.
|
||||

|
||||
|
||||

|
||||
## Secrets Overview
|
||||
|
||||
The supported project types are:
|
||||
The **Secrets Overview** page captures a birds-eye-view of secrets and [folders](./folder) across environments.
|
||||
This is useful for comparing secrets, identifying if anything is missing, and making quick changes.
|
||||
|
||||
- [Secrets Management](/documentation/platform/secrets-mgmt/overview): Securely store, access, and distribute secrets across environments with fine-grained controls, automatic rotation, and audit logging.
|
||||
- [Secrets Scanning](/documentation/platform/secret-scanning/overview): Detect hardcoded secrets in code, CI pipelines, and infrastructure—integrated with GitHub, GitLab, Bitbucket, and more.
|
||||
- [Infisical PKI](/documentation/platform/pki/overview): Issue and manage X.509 certificates using protocols like EST, with support for internal and external CAs.
|
||||
- [Infisical SSH](/documentation/platform/ssh/overview): Provide short-lived SSH access to servers using certificate-based authentication, replacing static keys with policy-driven, time-bound control.
|
||||
- [Infisical KMS](/documentation/platform/kms/overview): Encrypt and decrypt data using centrally managed keys with enforced access policies and full audit visibility.
|
||||

|
||||
|
||||
## Roles and Access Control
|
||||
## Secrets Dashboard
|
||||
|
||||
[Users](/documentation/platform/identities/user-identities) and [machine identities](/documentation/platform/identities/machine-identities) must be added to a project to access its resources. Each identity is assigned a [project-level role](/documentation/platform/access-controls/role-based-access-controls#project-level-access-controls) that defines what they can manage—such as secrets, certificates, or SSH access. These roles apply to both individuals and [user groups](/documentation/platform/groups), enabling scalable access across teams and environments.
|
||||
The **Secrets Dashboard** page appears when you press to manage the secrets of a specific environment.
|
||||
|
||||
Project access is strictly scoped: only members of a project can view or manage its resources. If someone needs access but isn’t part of the project, they can submit an access request.
|
||||

|
||||
|
||||
Each project in Infisical has its own [access control model](/documentation/platform/access-controls/role-based-access-controls#project-level-access-controls), distinct from [organization-level access control](/documentation/platform/access-controls/role-based-access-controls#organization-level-access-controls). While organization roles govern broader administrative access, project-level roles control what users, groups, and machine identities can do within the boundaries of a specific project—such as managing secrets, issuing certificates, or configuring SSH access.
|
||||
### Secrets
|
||||
|
||||
Depending on the project type (e.g. Secrets Management, PKI, SSH), project-level access control supports advanced features like [temporary access](/documentation/platform/access-controls/temporary-access), [access requests](/documentation/platform/access-controls/access-requests), and [additional privileges](/documentation/platform/access-controls/additional-privileges).
|
||||
To add a secret, press **Add Secret** button at the top of the dashboard.
|
||||
|
||||

|
||||

|
||||
|
||||
To learn more about how permissions work in detail, refer to the [access control documentation](/documentation/platform/access-controls/overview).
|
||||
For a new project, it can be convenient to populate the dashboard by dropping a `.env` file into the provided pane as shown below:
|
||||
|
||||
## Audit Logs
|
||||

|
||||
|
||||
Infisical provides [audit logging](/documentation/platform/audit-logs) at the project level to help teams monitor activity and maintain accountability within a specific project. These logs capture all relevant events—such as secret access, certificate issuance, and SSH activity—that occur within the boundaries of that project.
|
||||
To delete a secret, hover over it and press the **X** button that appears on the right side.
|
||||
|
||||
Unlike the organization-level audit view, which aggregates logs across all projects in one centralized interface, the project-level audit view is scoped to a single project. This enables relevant project admins and contributors to review activity relevant to their work without having broader access to audit logs in other projects that they are not part of.
|
||||

|
||||
|
||||
## Project Settings
|
||||
To delete multiple secrets at once, hover over and select the secrets you'd like to delete
|
||||
and press the **Delete** button that appears at the top.
|
||||
|
||||
Each project has its own settings panel, with options that vary depending on the selected product type. These may include
|
||||
setup and configuration for environments, tags, behaviors, encryption strategies, and other options.
|
||||

|
||||
|
||||
Project settings are fully independent and reflect the capabilities of the associated product.
|
||||
### Search
|
||||
|
||||
To search for specific secrets by their key name, you can use the search bar.
|
||||
|
||||

|
||||
|
||||
To assist you with finding secrets, you can also group them by similar prefixes and filter them by tags (if applicable).
|
||||
|
||||

|
||||
|
||||
### Hide/Un-hide
|
||||
|
||||
To view/hide all secrets at once, toggle the hide or un-hide button.
|
||||
|
||||

|
||||
|
||||
### Download as .env
|
||||
|
||||
To download/export secrets back into a `.env` file, press the download button.
|
||||
|
||||

|
||||
|
||||
### Tags
|
||||
|
||||
To better organize similar secrets, hover over them and label them with a tag.
|
||||
|
||||

|
||||
|
||||
### Comments
|
||||
|
||||
To provide more context about a given secret, especially for your team, hover over it and press the comment button.
|
||||
|
||||

|
||||
|
||||
### Personal overrides
|
||||
|
||||
Infisical employs the concept of **shared** and **personal** secrets to address the need
|
||||
for common and custom secret values, or branching, amongst members of a team during software development.
|
||||
To provide a helpful analogy: A shared value is to a `main` branch as a personal value is to a custom branch.
|
||||
|
||||
Consider:
|
||||
|
||||
- A team with users A, B, user C.
|
||||
- A project with an environment containing a shared secret called D with the value E.
|
||||
|
||||
Suppose user A overrides the value of secret D with the value F.
|
||||
|
||||
Then:
|
||||
|
||||
- If user A fetches the secret D back, they get the value F.
|
||||
- If users B and C fetch the secret D back, they both get the value E.
|
||||
|
||||
<Info>
|
||||
Please keep in mind that secret reminders won't work with personal overrides.
|
||||
</Info>
|
||||
|
||||

|
||||
|
||||
### Drawer
|
||||
|
||||
To view the full details of each secret, you can hover over it and press on the ellipses button.
|
||||
|
||||

|
||||
|
||||
This opens up a side-drawer:
|
||||
|
||||

|
||||
|
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: "Secrets Management"
|
||||
sidebarTitle: "Overview"
|
||||
description: "Learn how to securely store, access, and manage sensitive application secrets."
|
||||
---
|
||||
|
||||
Infisical provides a flexible platform for managing application secrets — such as API keys, database credentials, application configuration, and more — across every stage of the development lifecycle from local development to production.
|
||||
|
||||
It helps teams eliminate hardcoded secrets, enforce access controls, and adopt secure workflows like secret rotation, dynamic secrets, and secrets syncs to external platforms.
|
||||
|
||||
Core capabilities include:
|
||||
|
||||
- Secret Stores: Secure, versioned storage scoped by [project](/documentation/platform/secrets-mgmt/project), [environment](/documentation/platform/secrets-mgmt/project#project-environments), and [path](/documentation/platform/folder).
|
||||
- [Access Control](/documentation/platform/access-controls/overview): Fine-grained, identity-aware permissions for users and machines
|
||||
- Secret Delivery: Access secrets via [CLI](/cli/overview), [SDKs](/sdks/overview) (Go, Node.js, Python, etc.), [HTTP API](/api-reference/overview/introduction), [agents](/integrations/platforms/infisical-agent), [Kubernetes Operator](/integrations/platforms/kubernetes/overview), [External Secrets Operator (ESO)](https://external-secrets.io/latest/provider/infisical), and more.
|
||||
- Lifecycle Automation: Automate [secret rotation](/documentation/platform/secret-rotation/overview), generate [dynamic secrets](/documentation/platform/dynamic-secrets/overview), and enforce [approval-based workflows](/documentation/platform/pr-workflows).
|
||||
- [Secrets Syncs](/integrations/secret-syncs/overview): Push secrets to external services like [GitHub](/integrations/secret-syncs/github), [GitLab](/integrations/secret-syncs/gitlab), [AWS Secrets Manager](/integrations/secret-syncs/aws-secrets-manager), [Vercel](/integrations/secret-syncs/vercel), and more.
|
@@ -1,115 +0,0 @@
|
||||
---
|
||||
title: "Projects"
|
||||
description: "Learn more and understand the concept of Infisical projects."
|
||||
---
|
||||
|
||||
A secrets management project in Infisical is a dedicated workspace for managing application secrets such as API keys, database credentials, configuration, etc. used by your applications.
|
||||
|
||||
Secrets are organized into a clear hierarchy of environments, folders, and individual secrets, making it easy to manage values across different stages of your development lifecycle (e.g., development, staging, production).
|
||||
|
||||
## Project environments
|
||||
|
||||
For both visual and organizational structure, Infisical allows splitting up secrets into environments (e.g., development, staging, production). In project settings, such environments can be
|
||||
customized depending on the intended use case.
|
||||
|
||||

|
||||
|
||||
## Secrets Overview
|
||||
|
||||
The **Secrets Overview** page captures a birds-eye-view of secrets and [folders](./folder) across environments.
|
||||
This is useful for comparing secrets, identifying if anything is missing, and making quick changes.
|
||||
|
||||

|
||||
|
||||
## Secrets Dashboard
|
||||
|
||||
The **Secrets Dashboard** page appears when you press to manage the secrets of a specific environment.
|
||||
|
||||

|
||||
|
||||
### Secrets
|
||||
|
||||
To add a secret, press **Add Secret** button at the top of the dashboard.
|
||||
|
||||

|
||||
|
||||
For a new project, it can be convenient to populate the dashboard by dropping a `.env` file into the provided pane as shown below:
|
||||
|
||||

|
||||
|
||||
To delete a secret, hover over it and press the **X** button that appears on the right side.
|
||||
|
||||

|
||||
|
||||
To delete multiple secrets at once, hover over and select the secrets you'd like to delete
|
||||
and press the **Delete** button that appears at the top.
|
||||
|
||||

|
||||
|
||||
### Search
|
||||
|
||||
To search for specific secrets by their key name, you can use the search bar.
|
||||
|
||||

|
||||
|
||||
To assist you with finding secrets, you can also group them by similar prefixes and filter them by tags (if applicable).
|
||||
|
||||

|
||||
|
||||
### Hide/Un-hide
|
||||
|
||||
To view/hide all secrets at once, toggle the hide or un-hide button.
|
||||
|
||||

|
||||
|
||||
### Download as .env
|
||||
|
||||
To download/export secrets back into a `.env` file, press the download button.
|
||||
|
||||

|
||||
|
||||
### Tags
|
||||
|
||||
To better organize similar secrets, hover over them and label them with a tag.
|
||||
|
||||

|
||||
|
||||
### Comments
|
||||
|
||||
To provide more context about a given secret, especially for your team, hover over it and press the comment button.
|
||||
|
||||

|
||||
|
||||
### Personal overrides
|
||||
|
||||
Infisical employs the concept of **shared** and **personal** secrets to address the need
|
||||
for common and custom secret values, or branching, amongst members of a team during software development.
|
||||
To provide a helpful analogy: A shared value is to a `main` branch as a personal value is to a custom branch.
|
||||
|
||||
Consider:
|
||||
|
||||
- A team with users A, B, user C.
|
||||
- A project with an environment containing a shared secret called D with the value E.
|
||||
|
||||
Suppose user A overrides the value of secret D with the value F.
|
||||
|
||||
Then:
|
||||
|
||||
- If user A fetches the secret D back, they get the value F.
|
||||
- If users B and C fetch the secret D back, they both get the value E.
|
||||
|
||||
<Info>
|
||||
Please keep in mind that secret reminders won't work with personal overrides.
|
||||
</Info>
|
||||
|
||||

|
||||
|
||||
### Drawer
|
||||
|
||||
To view the full details of each secret, you can hover over it and press on the ellipses button.
|
||||
|
||||

|
||||
|
||||
This opens up a side-drawer:
|
||||
|
||||

|
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: "Host Groups"
|
||||
title: "Infisical SSH"
|
||||
sidebarTitle: "Host Groups"
|
||||
description: "Learn how to organize SSH hosts into groups and manage access policies at scale."
|
||||
---
|
||||
|
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: "Overview"
|
||||
title: "Infisical SSH"
|
||||
sidebarTitle: "Overview"
|
||||
description: "Learn how to securely provision user SSH access to your infrastructure using SSH certificates."
|
||||
---
|
||||
|
Before Width: | Height: | Size: 541 KiB |
Before Width: | Height: | Size: 608 KiB |
Before Width: | Height: | Size: 513 KiB After Width: | Height: | Size: 484 KiB |
After Width: | Height: | Size: 1.4 MiB |
BIN
docs/images/platform/organization/organization-members-roles.png
Normal file
After Width: | Height: | Size: 116 KiB |
BIN
docs/images/platform/organization/organization-members.png
Normal file
After Width: | Height: | Size: 162 KiB |
Before Width: | Height: | Size: 692 KiB After Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 521 KiB |
BIN
docs/images/platform/organization/organization-settings-auth.png
Normal file
After Width: | Height: | Size: 352 KiB |
After Width: | Height: | Size: 993 KiB |
Before Width: | Height: | Size: 725 KiB |
BIN
docs/images/platform/organization/organization-usage-billing.png
Normal file
After Width: | Height: | Size: 1.4 MiB |
Before Width: | Height: | Size: 522 KiB |
Before Width: | Height: | Size: 691 KiB |
Before Width: | Height: | Size: 624 KiB |
Before Width: | Height: | Size: 468 KiB |
@@ -105,10 +105,13 @@ The templates hold an array of templates that will be rendered and injected into
|
||||
|
||||
|
||||
### Authentication
|
||||
The Infisical Agent Injector only supports Machine Identity [Kubernetes Auth](/documentation/platform/identities/kubernetes-auth) authentication at the moment.
|
||||
The Infisical Agent Injector supports Machine Identity [Kubernetes Auth](/documentation/platform/identities/kubernetes-auth) and [LDAP Auth](/documentation/platform/identities/ldap-auth) authentication.
|
||||
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Kubernetes Auth">
|
||||
|
||||
To configure Kubernetes Auth, you need to set the `auth.type` field to `kubernetes` and set the `auth.config.identity-id` to the ID of the machine identity you wish to use for authentication.
|
||||
|
||||
```yaml
|
||||
auth:
|
||||
type: "kubernetes"
|
||||
@@ -144,6 +147,52 @@ data:
|
||||
kubectl apply -f config-map.yaml
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="LDAP Auth">
|
||||
To configure LDAP Auth, you need to set the `auth.type` field to `ldap-auth` and set the `auth.config.identity-id` to the ID of the machine identity you wish to use for authentication. Configure the `auth.config.username` and `auth.config.password` to the username and password of the LDAP user to authenticate with.
|
||||
|
||||
```yaml
|
||||
auth:
|
||||
type: "ldap-auth"
|
||||
config:
|
||||
identity-id: "<your-infisical-machine-identity-id>"
|
||||
username: "<your-ldap-username>"
|
||||
password: "<your-ldap-password>"
|
||||
```
|
||||
|
||||
### Example ConfigMap
|
||||
```yaml config-map.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: demo-config-map
|
||||
data:
|
||||
config.yaml: |
|
||||
infisical:
|
||||
address: "https://app.infisical.com"
|
||||
auth:
|
||||
type: "ldap-auth"
|
||||
config:
|
||||
identity-id: "<your-infisical-machine-identity-id>"
|
||||
username: "<your-ldap-username>"
|
||||
password: "<your-ldap-password>"
|
||||
templates:
|
||||
- destination-path: "/path/to/save/secrets/file.txt"
|
||||
template-content: |
|
||||
{{- with secret "<your-project-id>" "dev" "/" }}
|
||||
{{- range . }}
|
||||
{{ .Key }}={{ .Value }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
```
|
||||
|
||||
```bash
|
||||
kubectl apply -f config-map.yaml
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
To use the config map in your pod, you will need to add the `org.infisical.com/agent-config-map` annotation to your pod's deployment. The value of the annotation is the name of the config map you created above.
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
|
@@ -65,7 +65,14 @@ Example values:
|
||||
default="false"
|
||||
optional
|
||||
>
|
||||
Determines whether your Infisical instance can automatically read the service account token of the pod it's running on. Used for features such as the IRSA auth method.
|
||||
Determines whether your Infisical instance can automatically read the service
|
||||
account token of the pod it's running on. Used for features such as the IRSA
|
||||
auth method.
|
||||
</ParamField>
|
||||
|
||||
<ParamField query="DISABLE_AUDIT_LOG_STORAGE" type="string" default="false">
|
||||
Disable storing audit logs in the database. This is useful if you're using
|
||||
audit log streams and don't want to store them in the database.
|
||||
</ParamField>
|
||||
|
||||
## CORS
|
||||
|
@@ -35,6 +35,7 @@ export type TServerConfig = {
|
||||
initialized: boolean;
|
||||
allowSignUp: boolean;
|
||||
allowedSignUpDomain?: string | null;
|
||||
disableAuditLogStorage: boolean;
|
||||
isMigrationModeOn?: boolean;
|
||||
trustSamlEmails: boolean;
|
||||
trustLdapEmails: boolean;
|
||||
|
@@ -11,6 +11,7 @@ export type TGitHubConnection = TRootAppConnection & { app: AppConnection.GitHub
|
||||
method: GitHubConnectionMethod.OAuth;
|
||||
credentials: {
|
||||
code: string;
|
||||
instanceType?: "cloud" | "server";
|
||||
host?: string;
|
||||
};
|
||||
}
|
||||
@@ -19,6 +20,7 @@ export type TGitHubConnection = TRootAppConnection & { app: AppConnection.GitHub
|
||||
credentials: {
|
||||
code: string;
|
||||
installationId: string;
|
||||
instanceType?: "cloud" | "server";
|
||||
host?: string;
|
||||
};
|
||||
}
|
||||
|
@@ -12,14 +12,15 @@ export const useCreateReminder = (secretId: string) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<Reminder, object, CreateReminderDTO>({
|
||||
mutationFn: async ({ message, repeatDays, nextReminderDate, recipients }) => {
|
||||
mutationFn: async ({ message, repeatDays, nextReminderDate, recipients, fromDate }) => {
|
||||
const { data } = await apiRequest.post<{ reminder: Reminder }>(
|
||||
`/api/v1/reminders/secrets/${secretId}`,
|
||||
{
|
||||
message,
|
||||
repeatDays,
|
||||
nextReminderDate,
|
||||
recipients
|
||||
recipients,
|
||||
fromDate
|
||||
}
|
||||
);
|
||||
return data.reminder;
|
||||
|
@@ -2,6 +2,7 @@ export type CreateReminderDTO = {
|
||||
message?: string | null;
|
||||
repeatDays?: number | null;
|
||||
nextReminderDate?: Date | null;
|
||||
fromDate?: Date | null;
|
||||
secretId: string;
|
||||
recipients?: string[];
|
||||
};
|
||||
|
@@ -5,4 +5,5 @@ export type ServerStatus = {
|
||||
secretScanningConfigured: boolean;
|
||||
redisConfigured: boolean;
|
||||
samlDefaultOrgSlug: string;
|
||||
auditLogStorageDisabled: boolean;
|
||||
};
|
||||
|
@@ -6,10 +6,11 @@ import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { CreateOrgModal } from "@app/components/organization/CreateOrgModal";
|
||||
import { Banner } from "@app/components/page-frames/Banner";
|
||||
import { useServerConfig } from "@app/context";
|
||||
import { useServerConfig, useSubscription } from "@app/context";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import { useFetchServerStatus } from "@app/hooks/api";
|
||||
|
||||
import { AuditLogBanner } from "./components/AuditLogBanner";
|
||||
import { InsecureConnectionBanner } from "./components/InsecureConnectionBanner";
|
||||
import { Navbar } from "./components/NavBar";
|
||||
import { OrgSidebar } from "./components/OrgSidebar";
|
||||
@@ -31,6 +32,7 @@ export const OrganizationLayout = () => {
|
||||
const containerHeight = config.pageFrameContent ? "h-[94vh]" : "h-screen";
|
||||
|
||||
const { data: serverDetails, isLoading } = useFetchServerStatus();
|
||||
const { subscription } = useSubscription();
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -41,6 +43,7 @@ export const OrganizationLayout = () => {
|
||||
<Navbar />
|
||||
{!isLoading && !serverDetails?.redisConfigured && <RedisBanner />}
|
||||
{!isLoading && !serverDetails?.emailConfigured && <SmtpBanner />}
|
||||
{!isLoading && subscription.auditLogs && <AuditLogBanner />}
|
||||
{!window.isSecureContext && <InsecureConnectionBanner />}
|
||||
<div className="flex flex-grow flex-col overflow-y-hidden md:flex-row">
|
||||
<OrgSidebar isHidden={isInsideProject} />
|
||||
|
@@ -0,0 +1,23 @@
|
||||
import { useOrganization } from "@app/context";
|
||||
import { useFetchServerStatus, useGetAuditLogStreams } from "@app/hooks/api";
|
||||
|
||||
import { OrgAlertBanner } from "../OrgAlertBanner";
|
||||
|
||||
export const AuditLogBanner = () => {
|
||||
const org = useOrganization();
|
||||
const { data: status, isLoading: isLoadingStatus } = useFetchServerStatus();
|
||||
const { data: streams, isLoading: isLoadingStreams } = useGetAuditLogStreams(org.currentOrg.id);
|
||||
|
||||
if (isLoadingStreams || isLoadingStatus || !streams) return null;
|
||||
|
||||
if (status?.auditLogStorageDisabled && !streams.length) {
|
||||
return (
|
||||
<OrgAlertBanner
|
||||
text="Attention: Audit logs storage is disabled but no audit log streams have been configured."
|
||||
link="https://infisical.com/docs/documentation/platform/audit-log-streams/audit-log-streams"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
@@ -0,0 +1 @@
|
||||
export * from "./AuditLogBanner";
|
@@ -48,9 +48,16 @@ const formSchema = genericAppConnectionFieldsSchema.extend({
|
||||
app: z.literal(AppConnection.GitHub),
|
||||
method: z.nativeEnum(GitHubConnectionMethod),
|
||||
credentials: z
|
||||
.object({
|
||||
.union([
|
||||
z.object({
|
||||
instanceType: z.literal("cloud").optional(),
|
||||
host: z.string().optional()
|
||||
}),
|
||||
z.object({
|
||||
instanceType: z.literal("server"),
|
||||
host: z.string().min(1, "Required")
|
||||
})
|
||||
])
|
||||
.optional()
|
||||
});
|
||||
|
||||
@@ -70,7 +77,10 @@ export const GitHubConnectionForm = ({ appConnection }: Props) => {
|
||||
defaultValues: appConnection ?? {
|
||||
app: AppConnection.GitHub,
|
||||
method: GitHubConnectionMethod.App,
|
||||
gatewayId: null
|
||||
gatewayId: null,
|
||||
credentials: {
|
||||
instanceType: "cloud"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -78,6 +88,7 @@ export const GitHubConnectionForm = ({ appConnection }: Props) => {
|
||||
handleSubmit,
|
||||
control,
|
||||
watch,
|
||||
setValue,
|
||||
formState: { isSubmitting, isDirty }
|
||||
} = form;
|
||||
|
||||
@@ -85,6 +96,7 @@ export const GitHubConnectionForm = ({ appConnection }: Props) => {
|
||||
const { data: gateways, isPending: isGatewaysLoading } = useQuery(gatewaysQueryKeys.list());
|
||||
|
||||
const selectedMethod = watch("method");
|
||||
const instanceType = watch("credentials.instanceType");
|
||||
|
||||
const onSubmit = (formData: FormData) => {
|
||||
setIsRedirecting(true);
|
||||
@@ -103,7 +115,7 @@ export const GitHubConnectionForm = ({ appConnection }: Props) => {
|
||||
switch (formData.method) {
|
||||
case GitHubConnectionMethod.App:
|
||||
window.location.assign(
|
||||
`${githubHost}/apps/${appClientSlug}/installations/new?state=${state}`
|
||||
`${githubHost}/${formData.credentials?.instanceType === "server" ? "github-apps" : "apps"}/${appClientSlug}/installations/new?state=${state}`
|
||||
);
|
||||
break;
|
||||
case GitHubConnectionMethod.OAuth:
|
||||
@@ -175,13 +187,54 @@ export const GitHubConnectionForm = ({ appConnection }: Props) => {
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
{subscription.gateway && (
|
||||
<Accordion type="single" collapsible className="w-full">
|
||||
<AccordionItem value="enterprise-options" className="data-[state=open]:border-none">
|
||||
<AccordionTrigger className="h-fit flex-none pl-1 text-sm">
|
||||
<div className="order-1 ml-3">GitHub Enterprise Options</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent childrenClassName="px-0">
|
||||
<Controller
|
||||
name="credentials.instanceType"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControl label="Instance Type">
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={(e) => {
|
||||
field.onChange(e);
|
||||
if (e === "cloud") {
|
||||
setValue("gatewayId", null);
|
||||
}
|
||||
}}
|
||||
className="w-full border border-mineshaft-500"
|
||||
dropdownContainerClassName="max-w-none"
|
||||
placeholder="Enterprise Cloud"
|
||||
position="popper"
|
||||
>
|
||||
<SelectItem value="cloud">Enterprise Cloud</SelectItem>
|
||||
<SelectItem value="server">Enterprise Server</SelectItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
name="credentials.host"
|
||||
control={control}
|
||||
shouldUnregister
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error?.message)}
|
||||
label="Instance Hostname"
|
||||
isOptional={instanceType === "cloud"}
|
||||
isRequired={instanceType === "server"}
|
||||
>
|
||||
<Input {...field} placeholder="github.com" />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
{subscription.gateway && instanceType === "server" && (
|
||||
<OrgPermissionCan
|
||||
I={OrgGatewayPermissionActions.AttachGateways}
|
||||
a={OrgPermissionSubjects.Gateway}
|
||||
@@ -190,7 +243,6 @@ export const GitHubConnectionForm = ({ appConnection }: Props) => {
|
||||
<Controller
|
||||
control={control}
|
||||
name="gatewayId"
|
||||
defaultValue=""
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
isError={Boolean(error?.message)}
|
||||
@@ -204,7 +256,7 @@ export const GitHubConnectionForm = ({ appConnection }: Props) => {
|
||||
<div>
|
||||
<Select
|
||||
isDisabled={!isAllowed}
|
||||
value={value as string}
|
||||
value={value || (null as unknown as string)}
|
||||
onValueChange={onChange}
|
||||
className="w-full border border-mineshaft-500"
|
||||
dropdownContainerClassName="max-w-none"
|
||||
@@ -214,7 +266,7 @@ export const GitHubConnectionForm = ({ appConnection }: Props) => {
|
||||
>
|
||||
<SelectItem
|
||||
value={null as unknown as string}
|
||||
onClick={() => onChange(undefined)}
|
||||
onClick={() => onChange(null)}
|
||||
>
|
||||
Internet Gateway
|
||||
</SelectItem>
|
||||
@@ -231,25 +283,10 @@ export const GitHubConnectionForm = ({ appConnection }: Props) => {
|
||||
/>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
<Controller
|
||||
name="credentials.host"
|
||||
control={control}
|
||||
shouldUnregister
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error?.message)}
|
||||
label="Hostname"
|
||||
isOptional
|
||||
>
|
||||
<Input {...field} placeholder="github.com" />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
)}
|
||||
<div className="mt-8 flex items-center">
|
||||
<Button
|
||||
className="mr-4"
|
||||
|
@@ -408,6 +408,7 @@ export const OAuthCallbackPage = () => {
|
||||
credentials: {
|
||||
code: code as string,
|
||||
installationId: installationId as string,
|
||||
...(credentials?.instanceType && { instanceType: credentials.instanceType }),
|
||||
...(credentials?.host && { host: credentials.host })
|
||||
},
|
||||
gatewayId
|
||||
@@ -416,6 +417,7 @@ export const OAuthCallbackPage = () => {
|
||||
connectionId,
|
||||
credentials: {
|
||||
code: code as string,
|
||||
...(credentials?.instanceType && { instanceType: credentials.instanceType }),
|
||||
...(credentials?.host && { host: credentials.host })
|
||||
},
|
||||
gatewayId
|
||||
@@ -431,6 +433,7 @@ export const OAuthCallbackPage = () => {
|
||||
method: GitHubConnectionMethod.App,
|
||||
credentials: {
|
||||
code: code as string,
|
||||
...(credentials?.instanceType && { instanceType: credentials.instanceType }),
|
||||
installationId: installationId as string,
|
||||
...(credentials?.host && { host: credentials.host })
|
||||
},
|
||||
@@ -440,6 +443,7 @@ export const OAuthCallbackPage = () => {
|
||||
method: GitHubConnectionMethod.OAuth,
|
||||
credentials: {
|
||||
code: code as string,
|
||||
...(credentials?.instanceType && { instanceType: credentials.instanceType }),
|
||||
...(credentials?.host && { host: credentials.host })
|
||||
},
|
||||
gatewayId
|
||||
|
@@ -12,6 +12,7 @@ export const AuditLogsPage = () => {
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
<meta property="og:image" content="/images/message.png" />
|
||||
</Helmet>
|
||||
|
||||
<div className="flex h-full w-full justify-center bg-bunker-800 text-white">
|
||||
<div className="w-full max-w-7xl">
|
||||
<PageHeader
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { Fragment } from "react";
|
||||
import { faFile } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faCancel, faFile } from "@fortawesome/free-solid-svg-icons";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import {
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
Tr
|
||||
} from "@app/components/v2";
|
||||
import { Timezone } from "@app/helpers/datetime";
|
||||
import { useGetAuditLogs } from "@app/hooks/api";
|
||||
import { useFetchServerStatus, useGetAuditLogs } from "@app/hooks/api";
|
||||
import { TGetAuditLogsFilter } from "@app/hooks/api/auditLogs/types";
|
||||
|
||||
import { LogsTableRow } from "./LogsTableRow";
|
||||
@@ -30,6 +30,8 @@ type Props = {
|
||||
const AUDIT_LOG_LIMIT = 30;
|
||||
|
||||
export const LogsTable = ({ filter, refetchInterval, timezone }: Props) => {
|
||||
const { data: status } = useFetchServerStatus();
|
||||
|
||||
// Determine the project ID for filtering
|
||||
const filterProjectId =
|
||||
// Use the projectId from the filter if it exists
|
||||
@@ -79,7 +81,11 @@ export const LogsTable = ({ filter, refetchInterval, timezone }: Props) => {
|
||||
{isEmpty && (
|
||||
<Tr>
|
||||
<Td colSpan={3}>
|
||||
{status?.auditLogStorageDisabled ? (
|
||||
<EmptyState title="Audit log storage is disabled" icon={faCancel} />
|
||||
) : (
|
||||
<EmptyState title="No audit logs on file" icon={faFile} />
|
||||
)}
|
||||
</Td>
|
||||
</Tr>
|
||||
)}
|
||||
|
@@ -52,8 +52,15 @@ export const SecretSearchInput = ({
|
||||
if (activeIndex === 0 && e.key === "Enter") setIsOpen(true);
|
||||
}}
|
||||
autoComplete="off"
|
||||
className="input text-md h-[2.3rem] w-full rounded-md rounded-l-none bg-mineshaft-800 py-[0.375rem] pl-2.5 pr-8 text-gray-400 placeholder-mineshaft-50 placeholder-opacity-50 outline-none duration-200 placeholder:text-sm hover:ring-bunker-400/60 focus:bg-mineshaft-700/80 focus:ring-1 focus:ring-primary-400/50"
|
||||
placeholder="Search by secret, folder, tag or metadata..."
|
||||
className={twMerge(
|
||||
"input text-md h-[2.3rem] w-full rounded-md rounded-l-none bg-mineshaft-800 py-[0.375rem] pl-2.5 text-gray-400 placeholder-mineshaft-50 placeholder-opacity-50 outline-none duration-200 placeholder:text-sm hover:ring-bunker-400/60 focus:bg-mineshaft-700/80 focus:ring-1 focus:ring-primary-400/50",
|
||||
hasSearch ? "pr-8" : "pr-2.5"
|
||||
)}
|
||||
placeholder={
|
||||
isSingleEnv
|
||||
? "Search by secret, folder, tag or metadata..."
|
||||
: "Search by secret or folder name..."
|
||||
}
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
/>
|
||||
|
@@ -538,16 +538,20 @@ export const SecretApprovalRequestChanges = ({
|
||||
className="flex flex-nowrap items-center justify-between space-x-2 rounded border border-mineshaft-600 bg-mineshaft-800 px-2 py-1"
|
||||
key={`required-approver-${requiredApprover.userId}`}
|
||||
>
|
||||
<div className="flex text-sm">
|
||||
<Tooltip
|
||||
content={`${requiredApprover.firstName || ""} ${
|
||||
requiredApprover.lastName || ""
|
||||
}`}
|
||||
content={
|
||||
requiredApprover.firstName
|
||||
? `${requiredApprover.firstName || ""} ${requiredApprover.lastName || ""}`
|
||||
: undefined
|
||||
}
|
||||
position="left"
|
||||
sideOffset={10}
|
||||
>
|
||||
<span>{requiredApprover?.email}</span>
|
||||
</Tooltip>
|
||||
<div className="flex text-sm">
|
||||
<div>{requiredApprover?.email}</div>
|
||||
<span className="text-red">*</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<div>
|
||||
{reviewer?.comment && (
|
||||
<Tooltip className="max-w-lg break-words" content={reviewer.comment}>
|
||||
|
@@ -4,6 +4,7 @@ import { faClock, faTrash } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { format } from "date-fns";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -33,6 +34,7 @@ const MIN_REPEAT_DAYS = 1;
|
||||
const MAX_REPEAT_DAYS = 365;
|
||||
const DEFAULT_REPEAT_DAYS = 30;
|
||||
const DEFAULT_TEXTAREA_ROWS = 8;
|
||||
const ONE_DAY_IN_MILLIS = 86400000;
|
||||
|
||||
// Enums
|
||||
enum ReminderType {
|
||||
@@ -79,6 +81,11 @@ const ReminderFormSchema = z.object({
|
||||
.refine((data) => data > new Date(), { message: "Reminder date must be in the future" })
|
||||
.nullable()
|
||||
.optional(),
|
||||
fromDate: z.coerce
|
||||
.date()
|
||||
.refine((data) => data > new Date(), { message: "From date must be in the future" })
|
||||
.nullable()
|
||||
.optional(),
|
||||
reminderType: z.enum(["Recurring", "One Time"])
|
||||
});
|
||||
|
||||
@@ -86,7 +93,7 @@ export type TReminderFormSchema = z.infer<typeof ReminderFormSchema>;
|
||||
|
||||
// Custom hook for form state management
|
||||
const useReminderForm = (reminderData?: Reminder) => {
|
||||
const { repeatDays, message, nextReminderDate } = reminderData || {};
|
||||
const { repeatDays, message, nextReminderDate, fromDate } = reminderData || {};
|
||||
|
||||
const isEditMode = Boolean(reminderData);
|
||||
|
||||
@@ -96,9 +103,10 @@ const useReminderForm = (reminderData?: Reminder) => {
|
||||
message: message || "",
|
||||
nextReminderDate: nextReminderDate || null,
|
||||
reminderType: repeatDays ? ReminderType.Recurring : ReminderType.OneTime,
|
||||
recipients: []
|
||||
recipients: [],
|
||||
fromDate
|
||||
}),
|
||||
[repeatDays, message, nextReminderDate]
|
||||
[repeatDays, message, nextReminderDate, fromDate]
|
||||
);
|
||||
|
||||
return {
|
||||
@@ -153,7 +161,8 @@ export const CreateReminderForm = ({
|
||||
message: reminderData?.message || "",
|
||||
nextReminderDate: reminderData?.nextReminderDate || null,
|
||||
reminderType: reminderData?.repeatDays ? ReminderType.Recurring : ReminderType.OneTime,
|
||||
recipients: []
|
||||
recipients: [],
|
||||
fromDate: reminderData?.fromDate
|
||||
},
|
||||
resolver: zodResolver(ReminderFormSchema)
|
||||
});
|
||||
@@ -170,6 +179,7 @@ export const CreateReminderForm = ({
|
||||
|
||||
// Watch form values
|
||||
const reminderType = watch("reminderType");
|
||||
const fromDate = watch("fromDate");
|
||||
|
||||
// Invalidate queries helper
|
||||
const invalidateQueries = () => {
|
||||
@@ -195,7 +205,8 @@ export const CreateReminderForm = ({
|
||||
message: data.message,
|
||||
recipients: data.recipients?.map((r) => r.value) || [],
|
||||
secretId,
|
||||
nextReminderDate: data.nextReminderDate
|
||||
nextReminderDate: data.nextReminderDate,
|
||||
fromDate: data.fromDate
|
||||
});
|
||||
|
||||
invalidateQueries();
|
||||
@@ -243,6 +254,7 @@ export const CreateReminderForm = ({
|
||||
if (newType === ReminderType.Recurring) {
|
||||
setValue("repeatDays", DEFAULT_REPEAT_DAYS);
|
||||
setValue("nextReminderDate", null);
|
||||
setValue("fromDate", null);
|
||||
} else if (newType === ReminderType.OneTime) {
|
||||
const tomorrow = new Date();
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
@@ -259,6 +271,7 @@ export const CreateReminderForm = ({
|
||||
|
||||
const {
|
||||
repeatDays: repeatDaysInitial,
|
||||
fromDate: fromDateInitial,
|
||||
message,
|
||||
recipients,
|
||||
nextReminderDate: nextReminderDateInitial
|
||||
@@ -266,6 +279,7 @@ export const CreateReminderForm = ({
|
||||
|
||||
if (repeatDaysInitial) {
|
||||
setValue("repeatDays", repeatDaysInitial);
|
||||
setValue("fromDate", fromDateInitial);
|
||||
setValue("reminderType", ReminderType.Recurring);
|
||||
} else {
|
||||
setValue("reminderType", ReminderType.OneTime);
|
||||
@@ -323,6 +337,7 @@ export const CreateReminderForm = ({
|
||||
|
||||
{/* Conditional Fields Based on Reminder Type */}
|
||||
{reminderType === ReminderType.Recurring ? (
|
||||
<div className="grid grid-cols-[1fr,auto] gap-x-2">
|
||||
<Controller
|
||||
control={control}
|
||||
name="repeatDays"
|
||||
@@ -347,7 +362,6 @@ export const CreateReminderForm = ({
|
||||
max={MAX_REPEAT_DAYS}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
{/* Interval description */}
|
||||
<div
|
||||
className={twMerge(
|
||||
@@ -357,10 +371,39 @@ export const CreateReminderForm = ({
|
||||
>
|
||||
A reminder will be sent every{" "}
|
||||
{field.value && field.value > 1 ? `${field.value} days` : "day"}
|
||||
{fromDate ? ` starting from ${format(fromDate, "MM/dd/yy")}` : ""}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="fromDate"
|
||||
render={({ field, fieldState }) => (
|
||||
<FormControl
|
||||
className="mb-0"
|
||||
label="Start Date"
|
||||
tooltipText="When enabled, this date will be used as the start date for the first reminder"
|
||||
isError={Boolean(fieldState.error)}
|
||||
errorText={fieldState.error?.message || ""}
|
||||
>
|
||||
<DatePicker
|
||||
value={field.value || undefined}
|
||||
className="w-full"
|
||||
onChange={field.onChange}
|
||||
dateFormat="P"
|
||||
popUpProps={{
|
||||
open: isDatePickerOpen,
|
||||
onOpenChange: setIsDatePickerOpen
|
||||
}}
|
||||
popUpContentProps={{}}
|
||||
hideTime
|
||||
hidden={{ before: new Date(Date.now() + ONE_DAY_IN_MILLIS) }}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<Controller
|
||||
control={control}
|
||||
@@ -385,7 +428,7 @@ export const CreateReminderForm = ({
|
||||
}}
|
||||
popUpContentProps={{}}
|
||||
hideTime
|
||||
hidden={{ before: new Date(Date.now() + 86400000) }}
|
||||
hidden={{ before: new Date(Date.now() + ONE_DAY_IN_MILLIS) }}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
|