mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 14:27:59 +00:00
feat(secret-ref): implemented backend changes for multi env and folder in service token
This commit is contained in:
Generated
+223
-32
@@ -71,6 +71,7 @@
|
|||||||
"@types/node": "^18.11.3",
|
"@types/node": "^18.11.3",
|
||||||
"@types/nodemailer": "^6.4.6",
|
"@types/nodemailer": "^6.4.6",
|
||||||
"@types/passport": "^1.0.12",
|
"@types/passport": "^1.0.12",
|
||||||
|
"@types/picomatch": "^2.3.0",
|
||||||
"@types/supertest": "^2.0.12",
|
"@types/supertest": "^2.0.12",
|
||||||
"@types/swagger-jsdoc": "^6.0.1",
|
"@types/swagger-jsdoc": "^6.0.1",
|
||||||
"@types/swagger-ui-express": "^4.1.3",
|
"@types/swagger-ui-express": "^4.1.3",
|
||||||
@@ -2200,6 +2201,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@jest/reporters/node_modules/glob": {
|
||||||
|
"version": "7.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||||
|
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.1.1",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@jest/schemas": {
|
"node_modules/@jest/schemas": {
|
||||||
"version": "29.4.3",
|
"version": "29.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz",
|
||||||
@@ -3241,6 +3262,12 @@
|
|||||||
"@types/express": "*"
|
"@types/express": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/picomatch": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-O397rnSS9iQI4OirieAtsDqvCj4+3eY1J+EPdNTKuHuRWIfUoGyzX294o8C4KJYaLqgSrd2o60c5EqCU8Zv02g==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/prettier": {
|
"node_modules/@types/prettier": {
|
||||||
"version": "2.7.2",
|
"version": "2.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz",
|
||||||
@@ -5680,25 +5707,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/glob": {
|
|
||||||
"version": "7.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
|
||||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
|
||||||
"dependencies": {
|
|
||||||
"fs.realpath": "^1.0.0",
|
|
||||||
"inflight": "^1.0.4",
|
|
||||||
"inherits": "2",
|
|
||||||
"minimatch": "^3.1.1",
|
|
||||||
"once": "^1.3.0",
|
|
||||||
"path-is-absolute": "^1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/glob-parent": {
|
"node_modules/glob-parent": {
|
||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
||||||
@@ -6481,6 +6489,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jest-config/node_modules/glob": {
|
||||||
|
"version": "7.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||||
|
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.1.1",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jest-diff": {
|
"node_modules/jest-diff": {
|
||||||
"version": "29.5.0",
|
"version": "29.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz",
|
||||||
@@ -6776,6 +6804,26 @@
|
|||||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jest-runtime/node_modules/glob": {
|
||||||
|
"version": "7.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||||
|
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.1.1",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jest-snapshot": {
|
"node_modules/jest-snapshot": {
|
||||||
"version": "29.5.0",
|
"version": "29.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.5.0.tgz",
|
||||||
@@ -11071,6 +11119,25 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/rimraf/node_modules/glob": {
|
||||||
|
"version": "7.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||||
|
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.1.1",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ripemd160": {
|
"node_modules/ripemd160": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
|
||||||
@@ -11669,6 +11736,25 @@
|
|||||||
"node": ">=0.4.0"
|
"node": ">=0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/swagger-autogen/node_modules/glob": {
|
||||||
|
"version": "7.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||||
|
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.1.1",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/swagger-ui-dist": {
|
"node_modules/swagger-ui-dist": {
|
||||||
"version": "4.19.0",
|
"version": "4.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.19.0.tgz",
|
||||||
@@ -11723,6 +11809,26 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/test-exclude/node_modules/glob": {
|
||||||
|
"version": "7.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||||
|
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.1.1",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/text-hex": {
|
"node_modules/text-hex": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
|
||||||
@@ -14154,6 +14260,22 @@
|
|||||||
"string-length": "^4.0.1",
|
"string-length": "^4.0.1",
|
||||||
"strip-ansi": "^6.0.0",
|
"strip-ansi": "^6.0.0",
|
||||||
"v8-to-istanbul": "^9.0.1"
|
"v8-to-istanbul": "^9.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"glob": {
|
||||||
|
"version": "7.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||||
|
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.1.1",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@jest/schemas": {
|
"@jest/schemas": {
|
||||||
@@ -14988,6 +15110,12 @@
|
|||||||
"@types/express": "*"
|
"@types/express": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/picomatch": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-O397rnSS9iQI4OirieAtsDqvCj4+3eY1J+EPdNTKuHuRWIfUoGyzX294o8C4KJYaLqgSrd2o60c5EqCU8Zv02g==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/prettier": {
|
"@types/prettier": {
|
||||||
"version": "2.7.2",
|
"version": "2.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz",
|
||||||
@@ -16808,19 +16936,6 @@
|
|||||||
"integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
|
"integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"glob": {
|
|
||||||
"version": "7.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
|
||||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
|
||||||
"requires": {
|
|
||||||
"fs.realpath": "^1.0.0",
|
|
||||||
"inflight": "^1.0.4",
|
|
||||||
"inherits": "2",
|
|
||||||
"minimatch": "^3.1.1",
|
|
||||||
"once": "^1.3.0",
|
|
||||||
"path-is-absolute": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"glob-parent": {
|
"glob-parent": {
|
||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
||||||
@@ -17371,6 +17486,22 @@
|
|||||||
"pretty-format": "^29.5.0",
|
"pretty-format": "^29.5.0",
|
||||||
"slash": "^3.0.0",
|
"slash": "^3.0.0",
|
||||||
"strip-json-comments": "^3.1.1"
|
"strip-json-comments": "^3.1.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"glob": {
|
||||||
|
"version": "7.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||||
|
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.1.1",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jest-diff": {
|
"jest-diff": {
|
||||||
@@ -17606,6 +17737,22 @@
|
|||||||
"jest-util": "^29.5.0",
|
"jest-util": "^29.5.0",
|
||||||
"slash": "^3.0.0",
|
"slash": "^3.0.0",
|
||||||
"strip-bom": "^4.0.0"
|
"strip-bom": "^4.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"glob": {
|
||||||
|
"version": "7.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||||
|
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.1.1",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jest-snapshot": {
|
"jest-snapshot": {
|
||||||
@@ -20674,6 +20821,21 @@
|
|||||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"glob": "^7.1.3"
|
"glob": "^7.1.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"glob": {
|
||||||
|
"version": "7.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||||
|
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||||
|
"requires": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.1.1",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ripemd160": {
|
"ripemd160": {
|
||||||
@@ -21129,6 +21291,19 @@
|
|||||||
"version": "7.4.1",
|
"version": "7.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
|
||||||
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="
|
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="
|
||||||
|
},
|
||||||
|
"glob": {
|
||||||
|
"version": "7.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||||
|
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||||
|
"requires": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.1.1",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -21174,6 +21349,22 @@
|
|||||||
"@istanbuljs/schema": "^0.1.2",
|
"@istanbuljs/schema": "^0.1.2",
|
||||||
"glob": "^7.1.4",
|
"glob": "^7.1.4",
|
||||||
"minimatch": "^3.0.4"
|
"minimatch": "^3.0.4"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"glob": {
|
||||||
|
"version": "7.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||||
|
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.1.1",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"text-hex": {
|
"text-hex": {
|
||||||
|
|||||||
@@ -89,6 +89,7 @@
|
|||||||
"@types/node": "^18.11.3",
|
"@types/node": "^18.11.3",
|
||||||
"@types/nodemailer": "^6.4.6",
|
"@types/nodemailer": "^6.4.6",
|
||||||
"@types/passport": "^1.0.12",
|
"@types/passport": "^1.0.12",
|
||||||
|
"@types/picomatch": "^2.3.0",
|
||||||
"@types/supertest": "^2.0.12",
|
"@types/supertest": "^2.0.12",
|
||||||
"@types/swagger-jsdoc": "^6.0.1",
|
"@types/swagger-jsdoc": "^6.0.1",
|
||||||
"@types/swagger-ui-express": "^4.1.3",
|
"@types/swagger-ui-express": "^4.1.3",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
|
import picomatch from "picomatch";
|
||||||
import { ISecret, Secret, ServiceTokenData } from "../../models";
|
import { ISecret, Secret, ServiceTokenData } from "../../models";
|
||||||
import { IAction, SecretVersion } from "../../ee/models";
|
import { IAction, SecretVersion } from "../../ee/models";
|
||||||
import {
|
import {
|
||||||
@@ -9,7 +10,7 @@ import {
|
|||||||
ACTION_UPDATE_SECRETS,
|
ACTION_UPDATE_SECRETS,
|
||||||
ALGORITHM_AES_256_GCM,
|
ALGORITHM_AES_256_GCM,
|
||||||
ENCODING_SCHEME_UTF8,
|
ENCODING_SCHEME_UTF8,
|
||||||
SECRET_PERSONAL,
|
SECRET_PERSONAL
|
||||||
} from "../../variables";
|
} from "../../variables";
|
||||||
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
|
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
|
||||||
import { EventService } from "../../services";
|
import { EventService } from "../../services";
|
||||||
@@ -21,7 +22,7 @@ import { PERMISSION_WRITE_SECRETS } from "../../variables";
|
|||||||
import {
|
import {
|
||||||
userHasNoAbility,
|
userHasNoAbility,
|
||||||
userHasWorkspaceAccess,
|
userHasWorkspaceAccess,
|
||||||
userHasWriteOnlyAbility,
|
userHasWriteOnlyAbility
|
||||||
} from "../../ee/helpers/checkMembershipPermissions";
|
} from "../../ee/helpers/checkMembershipPermissions";
|
||||||
import Tag from "../../models/tag";
|
import Tag from "../../models/tag";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
@@ -30,7 +31,7 @@ import Folder from "../../models/folder";
|
|||||||
import {
|
import {
|
||||||
getFolderByPath,
|
getFolderByPath,
|
||||||
getFolderIdFromServiceToken,
|
getFolderIdFromServiceToken,
|
||||||
searchByFolderId,
|
searchByFolderId
|
||||||
} from "../../services/FolderService";
|
} from "../../services/FolderService";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,7 +48,7 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
environment,
|
environment,
|
||||||
requests,
|
requests,
|
||||||
secretPath,
|
secretPath
|
||||||
}: {
|
}: {
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
@@ -63,7 +64,7 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
// get secret blind index salt
|
// get secret blind index salt
|
||||||
const salt = await SecretService.getSecretBlindIndexSalt({
|
const salt = await SecretService.getSecretBlindIndexSalt({
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId)
|
||||||
});
|
});
|
||||||
|
|
||||||
const folders = await Folder.findOne({ workspace: workspaceId, environment });
|
const folders = await Folder.findOne({ workspace: workspaceId, environment });
|
||||||
@@ -73,22 +74,22 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (req.authData.authPayload instanceof ServiceTokenData) {
|
if (req.authData.authPayload instanceof ServiceTokenData) {
|
||||||
const { secretPath: serviceTkScopedSecretPath } = req.authData.authPayload;
|
const { scopes: tkScopes } = req.authData.authPayload;
|
||||||
|
const validScope = tkScopes.find(
|
||||||
|
(scope) =>
|
||||||
|
picomatch.isMatch(secretPath, scope.secretPath, { strictSlashes: false }) &&
|
||||||
|
scope.environment === environment
|
||||||
|
);
|
||||||
|
|
||||||
// in service token when not giving secretpath folderid must be root
|
// in service token when not giving secretpath folderid must be root
|
||||||
// this is to avoid giving folderid when service tokens are used
|
// this is to avoid giving folderid when service tokens are used
|
||||||
if (
|
if ((!secretPath && folderId !== "root") || (secretPath && !validScope)) {
|
||||||
(!secretPath && folderId !== "root") ||
|
|
||||||
(secretPath && secretPath !== serviceTkScopedSecretPath)
|
|
||||||
) {
|
|
||||||
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (secretPath) {
|
if (secretPath) {
|
||||||
folderId = await getFolderIdFromServiceToken(
|
folderId = await getFolderIdFromServiceToken(workspaceId, environment, secretPath);
|
||||||
workspaceId,
|
|
||||||
environment,
|
|
||||||
secretPath
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for await (const request of requests) {
|
for await (const request of requests) {
|
||||||
@@ -97,12 +98,10 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
|||||||
let secretBlindIndex = "";
|
let secretBlindIndex = "";
|
||||||
switch (request.method) {
|
switch (request.method) {
|
||||||
case "POST":
|
case "POST":
|
||||||
secretBlindIndex = await SecretService.generateSecretBlindIndexWithSalt(
|
secretBlindIndex = await SecretService.generateSecretBlindIndexWithSalt({
|
||||||
{
|
secretName: request.secret.secretName,
|
||||||
secretName: request.secret.secretName,
|
salt
|
||||||
salt,
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
createSecrets.push({
|
createSecrets.push({
|
||||||
...request.secret,
|
...request.secret,
|
||||||
@@ -113,16 +112,14 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
|||||||
folder: folderId,
|
folder: folderId,
|
||||||
secretBlindIndex,
|
secretBlindIndex,
|
||||||
algorithm: ALGORITHM_AES_256_GCM,
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
keyEncoding: ENCODING_SCHEME_UTF8,
|
keyEncoding: ENCODING_SCHEME_UTF8
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "PATCH":
|
case "PATCH":
|
||||||
secretBlindIndex = await SecretService.generateSecretBlindIndexWithSalt(
|
secretBlindIndex = await SecretService.generateSecretBlindIndexWithSalt({
|
||||||
{
|
secretName: request.secret.secretName,
|
||||||
secretName: request.secret.secretName,
|
salt
|
||||||
salt,
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
updateSecrets.push({
|
updateSecrets.push({
|
||||||
...request.secret,
|
...request.secret,
|
||||||
@@ -130,7 +127,7 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
|||||||
secretBlindIndex,
|
secretBlindIndex,
|
||||||
folder: folderId,
|
folder: folderId,
|
||||||
algorithm: ALGORITHM_AES_256_GCM,
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
keyEncoding: ENCODING_SCHEME_UTF8,
|
keyEncoding: ENCODING_SCHEME_UTF8
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "DELETE":
|
case "DELETE":
|
||||||
@@ -150,9 +147,9 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
|||||||
...n._doc,
|
...n._doc,
|
||||||
_id: new Types.ObjectId(),
|
_id: new Types.ObjectId(),
|
||||||
secret: n._id,
|
secret: n._id,
|
||||||
isDeleted: false,
|
isDeleted: false
|
||||||
};
|
};
|
||||||
}),
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
const addAction = (await EELogService.createAction({
|
const addAction = (await EELogService.createAction({
|
||||||
@@ -161,7 +158,7 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
|||||||
serviceAccountId: req.serviceAccount?._id,
|
serviceAccountId: req.serviceAccount?._id,
|
||||||
serviceTokenDataId: req.serviceTokenData?._id,
|
serviceTokenDataId: req.serviceTokenData?._id,
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
secretIds: createdSecrets.map((n) => n._id),
|
secretIds: createdSecrets.map((n) => n._id)
|
||||||
})) as IAction;
|
})) as IAction;
|
||||||
actions.push(addAction);
|
actions.push(addAction);
|
||||||
|
|
||||||
@@ -175,8 +172,8 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
folderId,
|
folderId,
|
||||||
channel,
|
channel,
|
||||||
userAgent: req.headers?.["user-agent"],
|
userAgent: req.headers?.["user-agent"]
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -195,7 +192,7 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
|||||||
listedSecretsObj = req.secrets.reduce(
|
listedSecretsObj = req.secrets.reduce(
|
||||||
(obj: any, secret: ISecret) => ({
|
(obj: any, secret: ISecret) => ({
|
||||||
...obj,
|
...obj,
|
||||||
[secret._id.toString()]: secret,
|
[secret._id.toString()]: secret
|
||||||
}),
|
}),
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
@@ -204,16 +201,16 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
|||||||
updateOne: {
|
updateOne: {
|
||||||
filter: {
|
filter: {
|
||||||
_id: new Types.ObjectId(u._id),
|
_id: new Types.ObjectId(u._id),
|
||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId)
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
$inc: {
|
$inc: {
|
||||||
version: 1,
|
version: 1
|
||||||
},
|
},
|
||||||
...u,
|
...u,
|
||||||
_id: new Types.ObjectId(u._id),
|
_id: new Types.ObjectId(u._id)
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
await Secret.bulkWrite(updateOperations);
|
await Secret.bulkWrite(updateOperations);
|
||||||
@@ -240,25 +237,25 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
|||||||
algorithm: ALGORITHM_AES_256_GCM,
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
keyEncoding: ENCODING_SCHEME_UTF8,
|
keyEncoding: ENCODING_SCHEME_UTF8,
|
||||||
tags: u.tags,
|
tags: u.tags,
|
||||||
folder: u.folder,
|
folder: u.folder
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
await EESecretService.addSecretVersions({
|
await EESecretService.addSecretVersions({
|
||||||
secretVersions,
|
secretVersions
|
||||||
});
|
});
|
||||||
|
|
||||||
updatedSecrets = await Secret.find({
|
updatedSecrets = await Secret.find({
|
||||||
_id: {
|
_id: {
|
||||||
$in: updateSecrets.map((u) => new Types.ObjectId(u._id)),
|
$in: updateSecrets.map((u) => new Types.ObjectId(u._id))
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateAction = (await EELogService.createAction({
|
const updateAction = (await EELogService.createAction({
|
||||||
name: ACTION_UPDATE_SECRETS,
|
name: ACTION_UPDATE_SECRETS,
|
||||||
userId: req.user._id,
|
userId: req.user._id,
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
secretIds: updatedSecrets.map((u) => u._id),
|
secretIds: updatedSecrets.map((u) => u._id)
|
||||||
})) as IAction;
|
})) as IAction;
|
||||||
actions.push(updateAction);
|
actions.push(updateAction);
|
||||||
|
|
||||||
@@ -272,8 +269,8 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
folderId,
|
folderId,
|
||||||
channel,
|
channel,
|
||||||
userAgent: req.headers?.["user-agent"],
|
userAgent: req.headers?.["user-agent"]
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -282,19 +279,19 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
|||||||
if (deleteSecrets.length > 0) {
|
if (deleteSecrets.length > 0) {
|
||||||
await Secret.deleteMany({
|
await Secret.deleteMany({
|
||||||
_id: {
|
_id: {
|
||||||
$in: deleteSecrets,
|
$in: deleteSecrets
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await EESecretService.markDeletedSecretVersions({
|
await EESecretService.markDeletedSecretVersions({
|
||||||
secretIds: deleteSecrets,
|
secretIds: deleteSecrets
|
||||||
});
|
});
|
||||||
|
|
||||||
const deleteAction = (await EELogService.createAction({
|
const deleteAction = (await EELogService.createAction({
|
||||||
name: ACTION_DELETE_SECRETS,
|
name: ACTION_DELETE_SECRETS,
|
||||||
userId: req.user._id,
|
userId: req.user._id,
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
secretIds: deleteSecrets,
|
secretIds: deleteSecrets
|
||||||
})) as IAction;
|
})) as IAction;
|
||||||
actions.push(deleteAction);
|
actions.push(deleteAction);
|
||||||
|
|
||||||
@@ -307,8 +304,8 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
|||||||
environment,
|
environment,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
channel: channel,
|
channel: channel,
|
||||||
userAgent: req.headers?.["user-agent"],
|
userAgent: req.headers?.["user-agent"]
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -320,22 +317,22 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
|||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
actions,
|
actions,
|
||||||
channel,
|
channel,
|
||||||
ipAddress: req.realIP,
|
ipAddress: req.realIP
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// // trigger event - push secrets
|
// // trigger event - push secrets
|
||||||
await EventService.handleEvent({
|
await EventService.handleEvent({
|
||||||
event: eventPushSecrets({
|
event: eventPushSecrets({
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId)
|
||||||
}),
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
// (EE) take a secret snapshot
|
// (EE) take a secret snapshot
|
||||||
await EESecretService.takeSecretSnapshot({
|
await EESecretService.takeSecretSnapshot({
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
folderId,
|
folderId
|
||||||
});
|
});
|
||||||
|
|
||||||
const resObj: { [key: string]: ISecret[] | string[] } = {};
|
const resObj: { [key: string]: ISecret[] | string[] } = {};
|
||||||
@@ -418,7 +415,7 @@ export const createSecrets = async (req: Request, res: Response) => {
|
|||||||
const {
|
const {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
environment,
|
environment,
|
||||||
secretPath,
|
secretPath
|
||||||
}: {
|
}: {
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
@@ -435,8 +432,7 @@ export const createSecrets = async (req: Request, res: Response) => {
|
|||||||
);
|
);
|
||||||
if (!hasAccess) {
|
if (!hasAccess) {
|
||||||
throw UnauthorizedRequestError({
|
throw UnauthorizedRequestError({
|
||||||
message:
|
message: "You do not have the necessary permission(s) perform this action"
|
||||||
"You do not have the necessary permission(s) perform this action",
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -449,28 +445,28 @@ export const createSecrets = async (req: Request, res: Response) => {
|
|||||||
// case: create 1 secret
|
// case: create 1 secret
|
||||||
listOfSecretsToCreate = [req.body.secrets];
|
listOfSecretsToCreate = [req.body.secrets];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.authData.authPayload instanceof ServiceTokenData) {
|
if (req.authData.authPayload instanceof ServiceTokenData) {
|
||||||
const { secretPath: serviceTkScopedSecretPath } = req.authData.authPayload;
|
const { scopes: tkScopes } = req.authData.authPayload;
|
||||||
|
const validScope = tkScopes.find(
|
||||||
|
(scope) =>
|
||||||
|
picomatch.isMatch(secretPath || "/", scope.secretPath, { strictSlashes: false }) &&
|
||||||
|
scope.environment === environment
|
||||||
|
);
|
||||||
|
|
||||||
// in service token when not giving secretpath folderid must be root
|
// in service token when not giving secretpath folderid must be root
|
||||||
// this is to avoid giving folderid when service tokens are used
|
// this is to avoid giving folderid when service tokens are used
|
||||||
if (
|
if ((!secretPath && folderId !== "root") || (secretPath && !validScope)) {
|
||||||
(!secretPath && folderId !== "root") ||
|
|
||||||
(secretPath && secretPath !== serviceTkScopedSecretPath)
|
|
||||||
) {
|
|
||||||
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (secretPath) {
|
if (secretPath) {
|
||||||
folderId = await getFolderIdFromServiceToken(
|
folderId = await getFolderIdFromServiceToken(workspaceId, environment, secretPath);
|
||||||
workspaceId,
|
|
||||||
environment,
|
|
||||||
secretPath
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// get secret blind index salt
|
// get secret blind index salt
|
||||||
const salt = await SecretService.getSecretBlindIndexSalt({
|
const salt = await SecretService.getSecretBlindIndexSalt({
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId)
|
||||||
});
|
});
|
||||||
|
|
||||||
type secretsToCreateType = {
|
type secretsToCreateType = {
|
||||||
@@ -502,15 +498,14 @@ export const createSecrets = async (req: Request, res: Response) => {
|
|||||||
secretCommentCiphertext,
|
secretCommentCiphertext,
|
||||||
secretCommentIV,
|
secretCommentIV,
|
||||||
secretCommentTag,
|
secretCommentTag,
|
||||||
tags,
|
tags
|
||||||
}: secretsToCreateType) => {
|
}: secretsToCreateType) => {
|
||||||
let secretBlindIndex;
|
let secretBlindIndex;
|
||||||
if (secretName) {
|
if (secretName) {
|
||||||
secretBlindIndex =
|
secretBlindIndex = await SecretService.generateSecretBlindIndexWithSalt({
|
||||||
await SecretService.generateSecretBlindIndexWithSalt({
|
secretName,
|
||||||
secretName,
|
salt
|
||||||
salt,
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -532,22 +527,22 @@ export const createSecrets = async (req: Request, res: Response) => {
|
|||||||
secretCommentTag,
|
secretCommentTag,
|
||||||
algorithm: ALGORITHM_AES_256_GCM,
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
keyEncoding: ENCODING_SCHEME_UTF8,
|
keyEncoding: ENCODING_SCHEME_UTF8,
|
||||||
tags,
|
tags
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const newlyCreatedSecrets: ISecret[] = (
|
const newlyCreatedSecrets: ISecret[] = (await Secret.insertMany(secretsToInsert)).map(
|
||||||
await Secret.insertMany(secretsToInsert)
|
(insertedSecret) => insertedSecret.toObject()
|
||||||
).map((insertedSecret) => insertedSecret.toObject());
|
);
|
||||||
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
// trigger event - push secrets
|
// trigger event - push secrets
|
||||||
await EventService.handleEvent({
|
await EventService.handleEvent({
|
||||||
event: eventPushSecrets({
|
event: eventPushSecrets({
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId)
|
||||||
}),
|
})
|
||||||
});
|
});
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
@@ -567,7 +562,7 @@ export const createSecrets = async (req: Request, res: Response) => {
|
|||||||
secretKeyTag,
|
secretKeyTag,
|
||||||
secretValueCiphertext,
|
secretValueCiphertext,
|
||||||
secretValueIV,
|
secretValueIV,
|
||||||
secretValueTag,
|
secretValueTag
|
||||||
}) =>
|
}) =>
|
||||||
new SecretVersion({
|
new SecretVersion({
|
||||||
secret: _id,
|
secret: _id,
|
||||||
@@ -586,9 +581,9 @@ export const createSecrets = async (req: Request, res: Response) => {
|
|||||||
secretValueTag,
|
secretValueTag,
|
||||||
folder: folderId,
|
folder: folderId,
|
||||||
algorithm: ALGORITHM_AES_256_GCM,
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
keyEncoding: ENCODING_SCHEME_UTF8,
|
keyEncoding: ENCODING_SCHEME_UTF8
|
||||||
})
|
})
|
||||||
),
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
const addAction = await EELogService.createAction({
|
const addAction = await EELogService.createAction({
|
||||||
@@ -597,7 +592,7 @@ export const createSecrets = async (req: Request, res: Response) => {
|
|||||||
serviceAccountId: req.serviceAccount?._id,
|
serviceAccountId: req.serviceAccount?._id,
|
||||||
serviceTokenDataId: req.serviceTokenData?._id,
|
serviceTokenDataId: req.serviceTokenData?._id,
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
secretIds: newlyCreatedSecrets.map((n) => n._id),
|
secretIds: newlyCreatedSecrets.map((n) => n._id)
|
||||||
});
|
});
|
||||||
|
|
||||||
// (EE) create (audit) log
|
// (EE) create (audit) log
|
||||||
@@ -609,14 +604,14 @@ export const createSecrets = async (req: Request, res: Response) => {
|
|||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
actions: [addAction],
|
actions: [addAction],
|
||||||
channel,
|
channel,
|
||||||
ipAddress: req.realIP,
|
ipAddress: req.realIP
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// (EE) take a secret snapshot
|
// (EE) take a secret snapshot
|
||||||
await EESecretService.takeSecretSnapshot({
|
await EESecretService.takeSecretSnapshot({
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
folderId,
|
folderId
|
||||||
});
|
});
|
||||||
|
|
||||||
const postHogClient = await TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
@@ -624,7 +619,7 @@ export const createSecrets = async (req: Request, res: Response) => {
|
|||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: "secrets added",
|
event: "secrets added",
|
||||||
distinctId: await TelemetryService.getDistinctId({
|
distinctId: await TelemetryService.getDistinctId({
|
||||||
authData: req.authData,
|
authData: req.authData
|
||||||
}),
|
}),
|
||||||
properties: {
|
properties: {
|
||||||
numberOfSecrets: listOfSecretsToCreate.length,
|
numberOfSecrets: listOfSecretsToCreate.length,
|
||||||
@@ -632,13 +627,13 @@ export const createSecrets = async (req: Request, res: Response) => {
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
channel: channel,
|
channel: channel,
|
||||||
folderId,
|
folderId,
|
||||||
userAgent: req.headers?.["user-agent"],
|
userAgent: req.headers?.["user-agent"]
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
secrets: newlyCreatedSecrets,
|
secrets: newlyCreatedSecrets
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -696,10 +691,7 @@ export const getSecrets = async (req: Request, res: Response) => {
|
|||||||
const environment = req.query.environment as string;
|
const environment = req.query.environment as string;
|
||||||
|
|
||||||
const folders = await Folder.findOne({ workspace: workspaceId, environment });
|
const folders = await Folder.findOne({ workspace: workspaceId, environment });
|
||||||
if (
|
if ((!folders && folderId && folderId !== "root") || (!folders && secretPath)) {
|
||||||
(!folders && folderId && folderId !== "root") ||
|
|
||||||
(!folders && secretPath)
|
|
||||||
) {
|
|
||||||
res.send({ secrets: [] });
|
res.send({ secrets: [] });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -712,13 +704,17 @@ export const getSecrets = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (req.authData.authPayload instanceof ServiceTokenData) {
|
if (req.authData.authPayload instanceof ServiceTokenData) {
|
||||||
const { secretPath: serviceTkScopedSecretPath } = req.authData.authPayload;
|
const { scopes: tkScopes } = req.authData.authPayload;
|
||||||
|
const validScope = tkScopes.find(
|
||||||
|
(scope) =>
|
||||||
|
picomatch.isMatch((secretPath as string) || "/", scope.secretPath, {
|
||||||
|
strictSlashes: false
|
||||||
|
}) && scope.environment === environment
|
||||||
|
);
|
||||||
|
|
||||||
// in service token when not giving secretpath folderid must be root
|
// in service token when not giving secretpath folderid must be root
|
||||||
// this is to avoid giving folderid when service tokens are used
|
// this is to avoid giving folderid when service tokens are used
|
||||||
if (
|
if ((!secretPath && folderId !== "root") || (secretPath && !validScope)) {
|
||||||
(!secretPath && folderId !== "root") ||
|
|
||||||
(secretPath && secretPath !== serviceTkScopedSecretPath)
|
|
||||||
) {
|
|
||||||
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -738,8 +734,7 @@ export const getSecrets = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
// query tags table to get all tags ids for the tag names for the given workspace
|
// query tags table to get all tags ids for the tag names for the given workspace
|
||||||
let tagIds = [];
|
let tagIds = [];
|
||||||
const tagNamesList =
|
const tagNamesList = typeof tagSlugs === "string" && tagSlugs !== "" ? tagSlugs.split(",") : [];
|
||||||
typeof tagSlugs === "string" && tagSlugs !== "" ? tagSlugs.split(",") : [];
|
|
||||||
if (tagNamesList != undefined && tagNamesList.length != 0) {
|
if (tagNamesList != undefined && tagNamesList.length != 0) {
|
||||||
const workspaceFromDB = await Tag.find({ workspace: workspaceId });
|
const workspaceFromDB = await Tag.find({ workspace: workspaceId });
|
||||||
tagIds = _.map(tagNamesList, (tagName: string) => {
|
tagIds = _.map(tagNamesList, (tagName: string) => {
|
||||||
@@ -762,8 +757,7 @@ export const getSecrets = async (req: Request, res: Response) => {
|
|||||||
);
|
);
|
||||||
if (hasNoAccess) {
|
if (hasNoAccess) {
|
||||||
throw UnauthorizedRequestError({
|
throw UnauthorizedRequestError({
|
||||||
message:
|
message: "You do not have the necessary permission(s) perform this action"
|
||||||
"You do not have the necessary permission(s) perform this action",
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -773,8 +767,8 @@ export const getSecrets = async (req: Request, res: Response) => {
|
|||||||
folder: folderId,
|
folder: folderId,
|
||||||
$or: [
|
$or: [
|
||||||
{ user: req.user._id }, // personal secrets for this user
|
{ user: req.user._id }, // personal secrets for this user
|
||||||
{ user: { $exists: false } }, // shared secrets from workspace
|
{ user: { $exists: false } } // shared secrets from workspace
|
||||||
],
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
if (tagIds.length > 0) {
|
if (tagIds.length > 0) {
|
||||||
@@ -801,8 +795,8 @@ export const getSecrets = async (req: Request, res: Response) => {
|
|||||||
environment,
|
environment,
|
||||||
$or: [
|
$or: [
|
||||||
{ user: userId }, // personal secrets for this user
|
{ user: userId }, // personal secrets for this user
|
||||||
{ user: { $exists: false } }, // shared secrets from workspace
|
{ user: { $exists: false } } // shared secrets from workspace
|
||||||
],
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
if (tagIds.length > 0) {
|
if (tagIds.length > 0) {
|
||||||
@@ -820,7 +814,7 @@ export const getSecrets = async (req: Request, res: Response) => {
|
|||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
environment,
|
environment,
|
||||||
folder: folderId,
|
folder: folderId,
|
||||||
user: { $exists: false }, // shared secrets only from workspace
|
user: { $exists: false } // shared secrets only from workspace
|
||||||
};
|
};
|
||||||
|
|
||||||
if (tagIds.length > 0) {
|
if (tagIds.length > 0) {
|
||||||
@@ -838,7 +832,7 @@ export const getSecrets = async (req: Request, res: Response) => {
|
|||||||
serviceAccountId: req.serviceAccount?._id,
|
serviceAccountId: req.serviceAccount?._id,
|
||||||
serviceTokenDataId: req.serviceTokenData?._id,
|
serviceTokenDataId: req.serviceTokenData?._id,
|
||||||
workspaceId: new Types.ObjectId(workspaceId as string),
|
workspaceId: new Types.ObjectId(workspaceId as string),
|
||||||
secretIds: secrets.map((n: any) => n._id),
|
secretIds: secrets.map((n: any) => n._id)
|
||||||
});
|
});
|
||||||
|
|
||||||
readAction &&
|
readAction &&
|
||||||
@@ -849,7 +843,7 @@ export const getSecrets = async (req: Request, res: Response) => {
|
|||||||
workspaceId: new Types.ObjectId(workspaceId as string),
|
workspaceId: new Types.ObjectId(workspaceId as string),
|
||||||
actions: [readAction],
|
actions: [readAction],
|
||||||
channel,
|
channel,
|
||||||
ipAddress: req.realIP,
|
ipAddress: req.realIP
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const postHogClient = await TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
@@ -857,7 +851,7 @@ export const getSecrets = async (req: Request, res: Response) => {
|
|||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: "secrets pulled",
|
event: "secrets pulled",
|
||||||
distinctId: await TelemetryService.getDistinctId({
|
distinctId: await TelemetryService.getDistinctId({
|
||||||
authData: req.authData,
|
authData: req.authData
|
||||||
}),
|
}),
|
||||||
properties: {
|
properties: {
|
||||||
numberOfSecrets: secrets.length,
|
numberOfSecrets: secrets.length,
|
||||||
@@ -865,13 +859,13 @@ export const getSecrets = async (req: Request, res: Response) => {
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
channel,
|
channel,
|
||||||
folderId,
|
folderId,
|
||||||
userAgent: req.headers?.["user-agent"],
|
userAgent: req.headers?.["user-agent"]
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
secrets,
|
secrets
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -925,9 +919,7 @@ export const updateSecrets = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
const channel = req.headers?.["user-agent"]?.toLowerCase().includes("mozilla")
|
const channel = req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli";
|
||||||
? "web"
|
|
||||||
: "cli";
|
|
||||||
|
|
||||||
interface PatchSecret {
|
interface PatchSecret {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -943,51 +935,47 @@ export const updateSecrets = async (req: Request, res: Response) => {
|
|||||||
tags: string[];
|
tags: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateOperationsToPerform = req.body.secrets.map(
|
const updateOperationsToPerform = req.body.secrets.map((secret: PatchSecret) => {
|
||||||
(secret: PatchSecret) => {
|
const {
|
||||||
const {
|
secretKeyCiphertext,
|
||||||
secretKeyCiphertext,
|
secretKeyIV,
|
||||||
secretKeyIV,
|
secretKeyTag,
|
||||||
secretKeyTag,
|
secretValueCiphertext,
|
||||||
secretValueCiphertext,
|
secretValueIV,
|
||||||
secretValueIV,
|
secretValueTag,
|
||||||
secretValueTag,
|
secretCommentCiphertext,
|
||||||
secretCommentCiphertext,
|
secretCommentIV,
|
||||||
secretCommentIV,
|
secretCommentTag,
|
||||||
secretCommentTag,
|
tags
|
||||||
tags,
|
} = secret;
|
||||||
} = secret;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
updateOne: {
|
updateOne: {
|
||||||
filter: { _id: new Types.ObjectId(secret.id) },
|
filter: { _id: new Types.ObjectId(secret.id) },
|
||||||
update: {
|
update: {
|
||||||
$inc: {
|
$inc: {
|
||||||
version: 1,
|
version: 1
|
||||||
},
|
|
||||||
secretKeyCiphertext,
|
|
||||||
secretKeyIV,
|
|
||||||
secretKeyTag,
|
|
||||||
secretValueCiphertext,
|
|
||||||
secretValueIV,
|
|
||||||
secretValueTag,
|
|
||||||
algorithm: ALGORITHM_AES_256_GCM,
|
|
||||||
keyEncoding: ENCODING_SCHEME_UTF8,
|
|
||||||
tags,
|
|
||||||
...(secretCommentCiphertext !== undefined &&
|
|
||||||
secretCommentIV &&
|
|
||||||
secretCommentTag
|
|
||||||
? {
|
|
||||||
secretCommentCiphertext,
|
|
||||||
secretCommentIV,
|
|
||||||
secretCommentTag,
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
},
|
},
|
||||||
},
|
secretKeyCiphertext,
|
||||||
};
|
secretKeyIV,
|
||||||
}
|
secretKeyTag,
|
||||||
);
|
secretValueCiphertext,
|
||||||
|
secretValueIV,
|
||||||
|
secretValueTag,
|
||||||
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
|
keyEncoding: ENCODING_SCHEME_UTF8,
|
||||||
|
tags,
|
||||||
|
...(secretCommentCiphertext !== undefined && secretCommentIV && secretCommentTag
|
||||||
|
? {
|
||||||
|
secretCommentCiphertext,
|
||||||
|
secretCommentIV,
|
||||||
|
secretCommentTag
|
||||||
|
}
|
||||||
|
: {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
await Secret.bulkWrite(updateOperationsToPerform);
|
await Secret.bulkWrite(updateOperationsToPerform);
|
||||||
|
|
||||||
@@ -1009,7 +997,7 @@ export const updateSecrets = async (req: Request, res: Response) => {
|
|||||||
secretCommentCiphertext,
|
secretCommentCiphertext,
|
||||||
secretCommentIV,
|
secretCommentIV,
|
||||||
secretCommentTag,
|
secretCommentTag,
|
||||||
tags,
|
tags
|
||||||
} = secretModificationsBySecretId[secret._id.toString()];
|
} = secretModificationsBySecretId[secret._id.toString()];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -1018,9 +1006,7 @@ export const updateSecrets = async (req: Request, res: Response) => {
|
|||||||
workspace: secret.workspace,
|
workspace: secret.workspace,
|
||||||
type: secret.type,
|
type: secret.type,
|
||||||
environment: secret.environment,
|
environment: secret.environment,
|
||||||
secretKeyCiphertext: secretKeyCiphertext
|
secretKeyCiphertext: secretKeyCiphertext ? secretKeyCiphertext : secret.secretKeyCiphertext,
|
||||||
? secretKeyCiphertext
|
|
||||||
: secret.secretKeyCiphertext,
|
|
||||||
secretKeyIV: secretKeyIV ? secretKeyIV : secret.secretKeyIV,
|
secretKeyIV: secretKeyIV ? secretKeyIV : secret.secretKeyIV,
|
||||||
secretKeyTag: secretKeyTag ? secretKeyTag : secret.secretKeyTag,
|
secretKeyTag: secretKeyTag ? secretKeyTag : secret.secretKeyTag,
|
||||||
secretValueCiphertext: secretValueCiphertext
|
secretValueCiphertext: secretValueCiphertext
|
||||||
@@ -1031,17 +1017,13 @@ export const updateSecrets = async (req: Request, res: Response) => {
|
|||||||
secretCommentCiphertext: secretCommentCiphertext
|
secretCommentCiphertext: secretCommentCiphertext
|
||||||
? secretCommentCiphertext
|
? secretCommentCiphertext
|
||||||
: secret.secretCommentCiphertext,
|
: secret.secretCommentCiphertext,
|
||||||
secretCommentIV: secretCommentIV
|
secretCommentIV: secretCommentIV ? secretCommentIV : secret.secretCommentIV,
|
||||||
? secretCommentIV
|
secretCommentTag: secretCommentTag ? secretCommentTag : secret.secretCommentTag,
|
||||||
: secret.secretCommentIV,
|
|
||||||
secretCommentTag: secretCommentTag
|
|
||||||
? secretCommentTag
|
|
||||||
: secret.secretCommentTag,
|
|
||||||
tags: tags ? tags : secret.tags,
|
tags: tags ? tags : secret.tags,
|
||||||
algorithm: ALGORITHM_AES_256_GCM,
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
keyEncoding: ENCODING_SCHEME_UTF8,
|
keyEncoding: ENCODING_SCHEME_UTF8
|
||||||
};
|
};
|
||||||
}),
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
await EESecretService.addSecretVersions(secretVersions);
|
await EESecretService.addSecretVersions(secretVersions);
|
||||||
@@ -1062,8 +1044,8 @@ export const updateSecrets = async (req: Request, res: Response) => {
|
|||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
await EventService.handleEvent({
|
await EventService.handleEvent({
|
||||||
event: eventPushSecrets({
|
event: eventPushSecrets({
|
||||||
workspaceId: new Types.ObjectId(key),
|
workspaceId: new Types.ObjectId(key)
|
||||||
}),
|
})
|
||||||
});
|
});
|
||||||
}, 10000);
|
}, 10000);
|
||||||
|
|
||||||
@@ -1073,7 +1055,7 @@ export const updateSecrets = async (req: Request, res: Response) => {
|
|||||||
serviceAccountId: req.serviceAccount?._id,
|
serviceAccountId: req.serviceAccount?._id,
|
||||||
serviceTokenDataId: req.serviceTokenData?._id,
|
serviceTokenDataId: req.serviceTokenData?._id,
|
||||||
workspaceId: new Types.ObjectId(key),
|
workspaceId: new Types.ObjectId(key),
|
||||||
secretIds: workspaceSecretObj[key].map((secret: ISecret) => secret._id),
|
secretIds: workspaceSecretObj[key].map((secret: ISecret) => secret._id)
|
||||||
});
|
});
|
||||||
|
|
||||||
// (EE) create (audit) log
|
// (EE) create (audit) log
|
||||||
@@ -1085,7 +1067,7 @@ export const updateSecrets = async (req: Request, res: Response) => {
|
|||||||
workspaceId: new Types.ObjectId(key),
|
workspaceId: new Types.ObjectId(key),
|
||||||
actions: [updateAction],
|
actions: [updateAction],
|
||||||
channel,
|
channel,
|
||||||
ipAddress: req.realIP,
|
ipAddress: req.realIP
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// (EE) take a secret snapshot
|
// (EE) take a secret snapshot
|
||||||
@@ -1101,15 +1083,15 @@ export const updateSecrets = async (req: Request, res: Response) => {
|
|||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: "secrets modified",
|
event: "secrets modified",
|
||||||
distinctId: await TelemetryService.getDistinctId({
|
distinctId: await TelemetryService.getDistinctId({
|
||||||
authData: req.authData,
|
authData: req.authData
|
||||||
}),
|
}),
|
||||||
properties: {
|
properties: {
|
||||||
numberOfSecrets: workspaceSecretObj[key].length,
|
numberOfSecrets: workspaceSecretObj[key].length,
|
||||||
environment: workspaceSecretObj[key][0].environment,
|
environment: workspaceSecretObj[key][0].environment,
|
||||||
workspaceId: key,
|
workspaceId: key,
|
||||||
channel: channel,
|
channel: channel,
|
||||||
userAgent: req.headers?.["user-agent"],
|
userAgent: req.headers?.["user-agent"]
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1117,9 +1099,9 @@ export const updateSecrets = async (req: Request, res: Response) => {
|
|||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
secrets: await Secret.find({
|
secrets: await Secret.find({
|
||||||
_id: {
|
_id: {
|
||||||
$in: req.secrets.map((secret: ISecret) => secret._id),
|
$in: req.secrets.map((secret: ISecret) => secret._id)
|
||||||
},
|
}
|
||||||
}),
|
})
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1179,12 +1161,12 @@ export const deleteSecrets = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
await Secret.deleteMany({
|
await Secret.deleteMany({
|
||||||
_id: {
|
_id: {
|
||||||
$in: toDelete,
|
$in: toDelete
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await EESecretService.markDeletedSecretVersions({
|
await EESecretService.markDeletedSecretVersions({
|
||||||
secretIds: toDelete,
|
secretIds: toDelete
|
||||||
});
|
});
|
||||||
|
|
||||||
// group secrets into workspaces so deleted secrets can
|
// group secrets into workspaces so deleted secrets can
|
||||||
@@ -1202,8 +1184,8 @@ export const deleteSecrets = async (req: Request, res: Response) => {
|
|||||||
// trigger event - push secrets
|
// trigger event - push secrets
|
||||||
await EventService.handleEvent({
|
await EventService.handleEvent({
|
||||||
event: eventPushSecrets({
|
event: eventPushSecrets({
|
||||||
workspaceId: new Types.ObjectId(key),
|
workspaceId: new Types.ObjectId(key)
|
||||||
}),
|
})
|
||||||
});
|
});
|
||||||
const deleteAction = await EELogService.createAction({
|
const deleteAction = await EELogService.createAction({
|
||||||
name: ACTION_DELETE_SECRETS,
|
name: ACTION_DELETE_SECRETS,
|
||||||
@@ -1211,7 +1193,7 @@ export const deleteSecrets = async (req: Request, res: Response) => {
|
|||||||
serviceAccountId: req.serviceAccount?._id,
|
serviceAccountId: req.serviceAccount?._id,
|
||||||
serviceTokenDataId: req.serviceTokenData?._id,
|
serviceTokenDataId: req.serviceTokenData?._id,
|
||||||
workspaceId: new Types.ObjectId(key),
|
workspaceId: new Types.ObjectId(key),
|
||||||
secretIds: workspaceSecretObj[key].map((secret: ISecret) => secret._id),
|
secretIds: workspaceSecretObj[key].map((secret: ISecret) => secret._id)
|
||||||
});
|
});
|
||||||
|
|
||||||
// (EE) create (audit) log
|
// (EE) create (audit) log
|
||||||
@@ -1223,7 +1205,7 @@ export const deleteSecrets = async (req: Request, res: Response) => {
|
|||||||
workspaceId: new Types.ObjectId(key),
|
workspaceId: new Types.ObjectId(key),
|
||||||
actions: [deleteAction],
|
actions: [deleteAction],
|
||||||
channel,
|
channel,
|
||||||
ipAddress: req.realIP,
|
ipAddress: req.realIP
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// (EE) take a secret snapshot
|
// (EE) take a secret snapshot
|
||||||
@@ -1237,20 +1219,20 @@ export const deleteSecrets = async (req: Request, res: Response) => {
|
|||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: "secrets deleted",
|
event: "secrets deleted",
|
||||||
distinctId: await TelemetryService.getDistinctId({
|
distinctId: await TelemetryService.getDistinctId({
|
||||||
authData: req.authData,
|
authData: req.authData
|
||||||
}),
|
}),
|
||||||
properties: {
|
properties: {
|
||||||
numberOfSecrets: workspaceSecretObj[key].length,
|
numberOfSecrets: workspaceSecretObj[key].length,
|
||||||
environment: workspaceSecretObj[key][0].environment,
|
environment: workspaceSecretObj[key][0].environment,
|
||||||
workspaceId: key,
|
workspaceId: key,
|
||||||
channel: channel,
|
channel: channel,
|
||||||
userAgent: req.headers?.["user-agent"],
|
userAgent: req.headers?.["user-agent"]
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
secrets: req.secrets,
|
secrets: req.secrets
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,10 +2,7 @@ import { Request, Response } from "express";
|
|||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
import bcrypt from "bcrypt";
|
import bcrypt from "bcrypt";
|
||||||
import { ServiceAccount, ServiceTokenData, User } from "../../models";
|
import { ServiceAccount, ServiceTokenData, User } from "../../models";
|
||||||
import {
|
import { AUTH_MODE_JWT, AUTH_MODE_SERVICE_ACCOUNT } from "../../variables";
|
||||||
AUTH_MODE_JWT,
|
|
||||||
AUTH_MODE_SERVICE_ACCOUNT,
|
|
||||||
} from "../../variables";
|
|
||||||
import { getSaltRounds } from "../../config";
|
import { getSaltRounds } from "../../config";
|
||||||
import { BadRequestError } from "../../utils/errors";
|
import { BadRequestError } from "../../utils/errors";
|
||||||
import Folder from "../../models/folder";
|
import Folder from "../../models/folder";
|
||||||
@@ -46,14 +43,13 @@ export const getServiceTokenData = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
if (!(req.authData.authPayload instanceof ServiceTokenData))
|
if (!(req.authData.authPayload instanceof ServiceTokenData))
|
||||||
throw BadRequestError({
|
throw BadRequestError({
|
||||||
message: "Failed accepted client validation for service token data",
|
message: "Failed accepted client validation for service token data"
|
||||||
});
|
});
|
||||||
|
|
||||||
const serviceTokenData = await ServiceTokenData.findById(
|
const serviceTokenData = await ServiceTokenData.findById(req.authData.authPayload._id)
|
||||||
req.authData.authPayload._id
|
|
||||||
)
|
|
||||||
.select("+encryptedKey +iv +tag")
|
.select("+encryptedKey +iv +tag")
|
||||||
.populate("user").lean();
|
.populate("user")
|
||||||
|
.lean();
|
||||||
|
|
||||||
return res.status(200).json(serviceTokenData);
|
return res.status(200).json(serviceTokenData);
|
||||||
};
|
};
|
||||||
@@ -68,29 +64,7 @@ export const getServiceTokenData = async (req: Request, res: Response) => {
|
|||||||
export const createServiceTokenData = async (req: Request, res: Response) => {
|
export const createServiceTokenData = async (req: Request, res: Response) => {
|
||||||
let serviceTokenData;
|
let serviceTokenData;
|
||||||
|
|
||||||
const {
|
const { name, workspaceId, encryptedKey, iv, tag, expiresIn, permissions, scopes } = req.body;
|
||||||
name,
|
|
||||||
workspaceId,
|
|
||||||
environment,
|
|
||||||
encryptedKey,
|
|
||||||
iv,
|
|
||||||
tag,
|
|
||||||
expiresIn,
|
|
||||||
secretPath,
|
|
||||||
permissions,
|
|
||||||
} = req.body;
|
|
||||||
|
|
||||||
const folders = await Folder.findOne({
|
|
||||||
workspace: workspaceId,
|
|
||||||
environment,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (folders) {
|
|
||||||
const folder = getFolderByPath(folders.nodes, secretPath);
|
|
||||||
if (folder == undefined) {
|
|
||||||
throw BadRequestError({ message: "Path for service token does not exist" })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const secret = crypto.randomBytes(16).toString("hex");
|
const secret = crypto.randomBytes(16).toString("hex");
|
||||||
const secretHash = await bcrypt.hash(secret, await getSaltRounds());
|
const secretHash = await bcrypt.hash(secret, await getSaltRounds());
|
||||||
@@ -103,10 +77,7 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
let user, serviceAccount;
|
let user, serviceAccount;
|
||||||
|
|
||||||
if (
|
if (req.authData.authMode === AUTH_MODE_JWT && req.authData.authPayload instanceof User) {
|
||||||
req.authData.authMode === AUTH_MODE_JWT &&
|
|
||||||
req.authData.authPayload instanceof User
|
|
||||||
) {
|
|
||||||
user = req.authData.authPayload._id;
|
user = req.authData.authPayload._id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,17 +91,16 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
|
|||||||
serviceTokenData = await new ServiceTokenData({
|
serviceTokenData = await new ServiceTokenData({
|
||||||
name,
|
name,
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
environment,
|
|
||||||
user,
|
user,
|
||||||
serviceAccount,
|
serviceAccount,
|
||||||
|
scopes,
|
||||||
lastUsed: new Date(),
|
lastUsed: new Date(),
|
||||||
expiresAt,
|
expiresAt,
|
||||||
secretHash,
|
secretHash,
|
||||||
encryptedKey,
|
encryptedKey,
|
||||||
iv,
|
iv,
|
||||||
tag,
|
tag,
|
||||||
secretPath,
|
permissions
|
||||||
permissions,
|
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
// return service token data without sensitive data
|
// return service token data without sensitive data
|
||||||
@@ -142,7 +112,7 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
serviceToken,
|
serviceToken,
|
||||||
serviceTokenData,
|
serviceTokenData
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -155,11 +125,9 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
|
|||||||
export const deleteServiceTokenData = async (req: Request, res: Response) => {
|
export const deleteServiceTokenData = async (req: Request, res: Response) => {
|
||||||
const { serviceTokenDataId } = req.params;
|
const { serviceTokenDataId } = req.params;
|
||||||
|
|
||||||
const serviceTokenData = await ServiceTokenData.findByIdAndDelete(
|
const serviceTokenData = await ServiceTokenData.findByIdAndDelete(serviceTokenDataId);
|
||||||
serviceTokenDataId
|
|
||||||
);
|
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
serviceTokenData,
|
serviceTokenData
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
+161
-175
@@ -4,21 +4,16 @@ import {
|
|||||||
DeleteSecretParams,
|
DeleteSecretParams,
|
||||||
GetSecretParams,
|
GetSecretParams,
|
||||||
GetSecretsParams,
|
GetSecretsParams,
|
||||||
UpdateSecretParams,
|
UpdateSecretParams
|
||||||
} from "../interfaces/services/SecretService";
|
} from "../interfaces/services/SecretService";
|
||||||
import {
|
import { ISecret, Secret, SecretBlindIndexData, ServiceTokenData } from "../models";
|
||||||
ISecret,
|
|
||||||
Secret,
|
|
||||||
SecretBlindIndexData,
|
|
||||||
ServiceTokenData,
|
|
||||||
} from "../models";
|
|
||||||
import { SecretVersion } from "../ee/models";
|
import { SecretVersion } from "../ee/models";
|
||||||
import {
|
import {
|
||||||
BadRequestError,
|
BadRequestError,
|
||||||
InternalServerError,
|
InternalServerError,
|
||||||
SecretBlindIndexDataNotFoundError,
|
SecretBlindIndexDataNotFoundError,
|
||||||
SecretNotFoundError,
|
SecretNotFoundError,
|
||||||
UnauthorizedRequestError,
|
UnauthorizedRequestError
|
||||||
} from "../utils/errors";
|
} from "../utils/errors";
|
||||||
import {
|
import {
|
||||||
ACTION_ADD_SECRETS,
|
ACTION_ADD_SECRETS,
|
||||||
@@ -29,51 +24,42 @@ import {
|
|||||||
ENCODING_SCHEME_BASE64,
|
ENCODING_SCHEME_BASE64,
|
||||||
ENCODING_SCHEME_UTF8,
|
ENCODING_SCHEME_UTF8,
|
||||||
SECRET_PERSONAL,
|
SECRET_PERSONAL,
|
||||||
SECRET_SHARED,
|
SECRET_SHARED
|
||||||
} from "../variables";
|
} from "../variables";
|
||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
import * as argon2 from "argon2";
|
import * as argon2 from "argon2";
|
||||||
import {
|
import {
|
||||||
decryptSymmetric128BitHexKeyUTF8,
|
decryptSymmetric128BitHexKeyUTF8,
|
||||||
encryptSymmetric128BitHexKeyUTF8,
|
encryptSymmetric128BitHexKeyUTF8
|
||||||
} from "../utils/crypto";
|
} from "../utils/crypto";
|
||||||
import { TelemetryService } from "../services";
|
import { TelemetryService } from "../services";
|
||||||
import { client, getEncryptionKey, getRootEncryptionKey } from "../config";
|
import { client, getEncryptionKey, getRootEncryptionKey } from "../config";
|
||||||
import { EELogService, EESecretService } from "../ee/services";
|
import { EELogService, EESecretService } from "../ee/services";
|
||||||
import {
|
import { getAuthDataPayloadIdObj, getAuthDataPayloadUserObj } from "../utils/auth";
|
||||||
getAuthDataPayloadIdObj,
|
|
||||||
getAuthDataPayloadUserObj,
|
|
||||||
} from "../utils/auth";
|
|
||||||
import { getFolderIdFromServiceToken } from "../services/FolderService";
|
import { getFolderIdFromServiceToken } from "../services/FolderService";
|
||||||
|
import picomatch from "picomatch";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an object containing secret [secret] but with its value, key, comment decrypted.
|
* Returns an object containing secret [secret] but with its value, key, comment decrypted.
|
||||||
*
|
*
|
||||||
* Precondition: the workspace for secret [secret] must have E2EE disabled
|
* Precondition: the workspace for secret [secret] must have E2EE disabled
|
||||||
* @param {ISecret} secret - secret to repackage to raw
|
* @param {ISecret} secret - secret to repackage to raw
|
||||||
* @param {String} key - symmetric key to use to decrypt secret
|
* @param {String} key - symmetric key to use to decrypt secret
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const repackageSecretToRaw = ({
|
export const repackageSecretToRaw = ({ secret, key }: { secret: ISecret; key: string }) => {
|
||||||
secret,
|
|
||||||
key,
|
|
||||||
}: {
|
|
||||||
secret: ISecret;
|
|
||||||
key: string;
|
|
||||||
}) => {
|
|
||||||
|
|
||||||
const secretKey = decryptSymmetric128BitHexKeyUTF8({
|
const secretKey = decryptSymmetric128BitHexKeyUTF8({
|
||||||
ciphertext: secret.secretKeyCiphertext,
|
ciphertext: secret.secretKeyCiphertext,
|
||||||
iv: secret.secretKeyIV,
|
iv: secret.secretKeyIV,
|
||||||
tag: secret.secretKeyTag,
|
tag: secret.secretKeyTag,
|
||||||
key,
|
key
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretValue = decryptSymmetric128BitHexKeyUTF8({
|
const secretValue = decryptSymmetric128BitHexKeyUTF8({
|
||||||
ciphertext: secret.secretValueCiphertext,
|
ciphertext: secret.secretValueCiphertext,
|
||||||
iv: secret.secretValueIV,
|
iv: secret.secretValueIV,
|
||||||
tag: secret.secretValueTag,
|
tag: secret.secretValueTag,
|
||||||
key,
|
key
|
||||||
});
|
});
|
||||||
|
|
||||||
let secretComment = "";
|
let secretComment = "";
|
||||||
@@ -83,11 +69,11 @@ export const repackageSecretToRaw = ({
|
|||||||
ciphertext: secret.secretCommentCiphertext,
|
ciphertext: secret.secretCommentCiphertext,
|
||||||
iv: secret.secretCommentIV,
|
iv: secret.secretCommentIV,
|
||||||
tag: secret.secretCommentTag,
|
tag: secret.secretCommentTag,
|
||||||
key,
|
key
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return ({
|
return {
|
||||||
_id: secret._id,
|
_id: secret._id,
|
||||||
version: secret.version,
|
version: secret.version,
|
||||||
workspace: secret.workspace,
|
workspace: secret.workspace,
|
||||||
@@ -96,9 +82,9 @@ export const repackageSecretToRaw = ({
|
|||||||
user: secret.user,
|
user: secret.user,
|
||||||
secretKey,
|
secretKey,
|
||||||
secretValue,
|
secretValue,
|
||||||
secretComment,
|
secretComment
|
||||||
});
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create secret blind index data containing encrypted blind index [salt]
|
* Create secret blind index data containing encrypted blind index [salt]
|
||||||
@@ -107,7 +93,7 @@ export const repackageSecretToRaw = ({
|
|||||||
* @param {Types.ObjectId} obj.workspaceId
|
* @param {Types.ObjectId} obj.workspaceId
|
||||||
*/
|
*/
|
||||||
export const createSecretBlindIndexDataHelper = async ({
|
export const createSecretBlindIndexDataHelper = async ({
|
||||||
workspaceId,
|
workspaceId
|
||||||
}: {
|
}: {
|
||||||
workspaceId: Types.ObjectId;
|
workspaceId: Types.ObjectId;
|
||||||
}) => {
|
}) => {
|
||||||
@@ -121,7 +107,7 @@ export const createSecretBlindIndexDataHelper = async ({
|
|||||||
const {
|
const {
|
||||||
ciphertext: encryptedSaltCiphertext,
|
ciphertext: encryptedSaltCiphertext,
|
||||||
iv: saltIV,
|
iv: saltIV,
|
||||||
tag: saltTag,
|
tag: saltTag
|
||||||
} = client.encryptSymmetric(salt, rootEncryptionKey);
|
} = client.encryptSymmetric(salt, rootEncryptionKey);
|
||||||
|
|
||||||
return await new SecretBlindIndexData({
|
return await new SecretBlindIndexData({
|
||||||
@@ -130,16 +116,16 @@ export const createSecretBlindIndexDataHelper = async ({
|
|||||||
saltIV,
|
saltIV,
|
||||||
saltTag,
|
saltTag,
|
||||||
algorithm: ALGORITHM_AES_256_GCM,
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
keyEncoding: ENCODING_SCHEME_BASE64,
|
keyEncoding: ENCODING_SCHEME_BASE64
|
||||||
}).save();
|
}).save();
|
||||||
} else {
|
} else {
|
||||||
const {
|
const {
|
||||||
ciphertext: encryptedSaltCiphertext,
|
ciphertext: encryptedSaltCiphertext,
|
||||||
iv: saltIV,
|
iv: saltIV,
|
||||||
tag: saltTag,
|
tag: saltTag
|
||||||
} = encryptSymmetric128BitHexKeyUTF8({
|
} = encryptSymmetric128BitHexKeyUTF8({
|
||||||
plaintext: salt,
|
plaintext: salt,
|
||||||
key: encryptionKey,
|
key: encryptionKey
|
||||||
});
|
});
|
||||||
|
|
||||||
return await new SecretBlindIndexData({
|
return await new SecretBlindIndexData({
|
||||||
@@ -148,7 +134,7 @@ export const createSecretBlindIndexDataHelper = async ({
|
|||||||
saltIV,
|
saltIV,
|
||||||
saltTag,
|
saltTag,
|
||||||
algorithm: ALGORITHM_AES_256_GCM,
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
keyEncoding: ENCODING_SCHEME_UTF8,
|
keyEncoding: ENCODING_SCHEME_UTF8
|
||||||
}).save();
|
}).save();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -160,7 +146,7 @@ export const createSecretBlindIndexDataHelper = async ({
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getSecretBlindIndexSaltHelper = async ({
|
export const getSecretBlindIndexSaltHelper = async ({
|
||||||
workspaceId,
|
workspaceId
|
||||||
}: {
|
}: {
|
||||||
workspaceId: Types.ObjectId;
|
workspaceId: Types.ObjectId;
|
||||||
}) => {
|
}) => {
|
||||||
@@ -168,36 +154,30 @@ export const getSecretBlindIndexSaltHelper = async ({
|
|||||||
const rootEncryptionKey = await getRootEncryptionKey();
|
const rootEncryptionKey = await getRootEncryptionKey();
|
||||||
|
|
||||||
const secretBlindIndexData = await SecretBlindIndexData.findOne({
|
const secretBlindIndexData = await SecretBlindIndexData.findOne({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId
|
||||||
}).select("+algorithm +keyEncoding");
|
}).select("+algorithm +keyEncoding");
|
||||||
|
|
||||||
if (!secretBlindIndexData) throw SecretBlindIndexDataNotFoundError();
|
if (!secretBlindIndexData) throw SecretBlindIndexDataNotFoundError();
|
||||||
|
|
||||||
if (
|
if (rootEncryptionKey && secretBlindIndexData.keyEncoding === ENCODING_SCHEME_BASE64) {
|
||||||
rootEncryptionKey &&
|
|
||||||
secretBlindIndexData.keyEncoding === ENCODING_SCHEME_BASE64
|
|
||||||
) {
|
|
||||||
return client.decryptSymmetric(
|
return client.decryptSymmetric(
|
||||||
secretBlindIndexData.encryptedSaltCiphertext,
|
secretBlindIndexData.encryptedSaltCiphertext,
|
||||||
rootEncryptionKey,
|
rootEncryptionKey,
|
||||||
secretBlindIndexData.saltIV,
|
secretBlindIndexData.saltIV,
|
||||||
secretBlindIndexData.saltTag
|
secretBlindIndexData.saltTag
|
||||||
);
|
);
|
||||||
} else if (
|
} else if (encryptionKey && secretBlindIndexData.keyEncoding === ENCODING_SCHEME_UTF8) {
|
||||||
encryptionKey &&
|
|
||||||
secretBlindIndexData.keyEncoding === ENCODING_SCHEME_UTF8
|
|
||||||
) {
|
|
||||||
// decrypt workspace salt
|
// decrypt workspace salt
|
||||||
return decryptSymmetric128BitHexKeyUTF8({
|
return decryptSymmetric128BitHexKeyUTF8({
|
||||||
ciphertext: secretBlindIndexData.encryptedSaltCiphertext,
|
ciphertext: secretBlindIndexData.encryptedSaltCiphertext,
|
||||||
iv: secretBlindIndexData.saltIV,
|
iv: secretBlindIndexData.saltIV,
|
||||||
tag: secretBlindIndexData.saltTag,
|
tag: secretBlindIndexData.saltTag,
|
||||||
key: encryptionKey,
|
key: encryptionKey
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
throw InternalServerError({
|
throw InternalServerError({
|
||||||
message: "Failed to obtain workspace salt needed for secret blind indexing",
|
message: "Failed to obtain workspace salt needed for secret blind indexing"
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -210,7 +190,7 @@ export const getSecretBlindIndexSaltHelper = async ({
|
|||||||
*/
|
*/
|
||||||
export const generateSecretBlindIndexWithSaltHelper = async ({
|
export const generateSecretBlindIndexWithSaltHelper = async ({
|
||||||
secretName,
|
secretName,
|
||||||
salt,
|
salt
|
||||||
}: {
|
}: {
|
||||||
secretName: string;
|
secretName: string;
|
||||||
salt: string;
|
salt: string;
|
||||||
@@ -224,7 +204,7 @@ export const generateSecretBlindIndexWithSaltHelper = async ({
|
|||||||
memoryCost: 65536, // default pool of 64 MiB per thread.
|
memoryCost: 65536, // default pool of 64 MiB per thread.
|
||||||
hashLength: 32,
|
hashLength: 32,
|
||||||
parallelism: 1,
|
parallelism: 1,
|
||||||
raw: true,
|
raw: true
|
||||||
})
|
})
|
||||||
).toString("base64");
|
).toString("base64");
|
||||||
|
|
||||||
@@ -240,7 +220,7 @@ export const generateSecretBlindIndexWithSaltHelper = async ({
|
|||||||
*/
|
*/
|
||||||
export const generateSecretBlindIndexHelper = async ({
|
export const generateSecretBlindIndexHelper = async ({
|
||||||
secretName,
|
secretName,
|
||||||
workspaceId,
|
workspaceId
|
||||||
}: {
|
}: {
|
||||||
secretName: string;
|
secretName: string;
|
||||||
workspaceId: Types.ObjectId;
|
workspaceId: Types.ObjectId;
|
||||||
@@ -250,16 +230,13 @@ export const generateSecretBlindIndexHelper = async ({
|
|||||||
const rootEncryptionKey = await getRootEncryptionKey();
|
const rootEncryptionKey = await getRootEncryptionKey();
|
||||||
|
|
||||||
const secretBlindIndexData = await SecretBlindIndexData.findOne({
|
const secretBlindIndexData = await SecretBlindIndexData.findOne({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId
|
||||||
}).select("+algorithm +keyEncoding");
|
}).select("+algorithm +keyEncoding");
|
||||||
|
|
||||||
if (!secretBlindIndexData) throw SecretBlindIndexDataNotFoundError();
|
if (!secretBlindIndexData) throw SecretBlindIndexDataNotFoundError();
|
||||||
|
|
||||||
let salt;
|
let salt;
|
||||||
if (
|
if (rootEncryptionKey && secretBlindIndexData.keyEncoding === ENCODING_SCHEME_BASE64) {
|
||||||
rootEncryptionKey &&
|
|
||||||
secretBlindIndexData.keyEncoding === ENCODING_SCHEME_BASE64
|
|
||||||
) {
|
|
||||||
salt = client.decryptSymmetric(
|
salt = client.decryptSymmetric(
|
||||||
secretBlindIndexData.encryptedSaltCiphertext,
|
secretBlindIndexData.encryptedSaltCiphertext,
|
||||||
rootEncryptionKey,
|
rootEncryptionKey,
|
||||||
@@ -269,32 +246,29 @@ export const generateSecretBlindIndexHelper = async ({
|
|||||||
|
|
||||||
const secretBlindIndex = await generateSecretBlindIndexWithSaltHelper({
|
const secretBlindIndex = await generateSecretBlindIndexWithSaltHelper({
|
||||||
secretName,
|
secretName,
|
||||||
salt,
|
salt
|
||||||
});
|
});
|
||||||
|
|
||||||
return secretBlindIndex;
|
return secretBlindIndex;
|
||||||
} else if (
|
} else if (encryptionKey && secretBlindIndexData.keyEncoding === ENCODING_SCHEME_UTF8) {
|
||||||
encryptionKey &&
|
|
||||||
secretBlindIndexData.keyEncoding === ENCODING_SCHEME_UTF8
|
|
||||||
) {
|
|
||||||
// decrypt workspace salt
|
// decrypt workspace salt
|
||||||
salt = decryptSymmetric128BitHexKeyUTF8({
|
salt = decryptSymmetric128BitHexKeyUTF8({
|
||||||
ciphertext: secretBlindIndexData.encryptedSaltCiphertext,
|
ciphertext: secretBlindIndexData.encryptedSaltCiphertext,
|
||||||
iv: secretBlindIndexData.saltIV,
|
iv: secretBlindIndexData.saltIV,
|
||||||
tag: secretBlindIndexData.saltTag,
|
tag: secretBlindIndexData.saltTag,
|
||||||
key: encryptionKey,
|
key: encryptionKey
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretBlindIndex = await generateSecretBlindIndexWithSaltHelper({
|
const secretBlindIndex = await generateSecretBlindIndexWithSaltHelper({
|
||||||
secretName,
|
secretName,
|
||||||
salt,
|
salt
|
||||||
});
|
});
|
||||||
|
|
||||||
return secretBlindIndex;
|
return secretBlindIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw InternalServerError({
|
throw InternalServerError({
|
||||||
message: "Failed to generate secret blind index",
|
message: "Failed to generate secret blind index"
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -323,38 +297,39 @@ export const createSecretHelper = async ({
|
|||||||
secretCommentCiphertext,
|
secretCommentCiphertext,
|
||||||
secretCommentIV,
|
secretCommentIV,
|
||||||
secretCommentTag,
|
secretCommentTag,
|
||||||
secretPath = "/",
|
secretPath = "/"
|
||||||
}: CreateSecretParams) => {
|
}: CreateSecretParams) => {
|
||||||
|
|
||||||
const secretBlindIndex = await generateSecretBlindIndexHelper({
|
const secretBlindIndex = await generateSecretBlindIndexHelper({
|
||||||
secretName,
|
secretName,
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId)
|
||||||
});
|
});
|
||||||
|
|
||||||
// if using service token filter towards the folderId by secretpath
|
// if using service token filter towards the folderId by secretpath
|
||||||
if (authData.authPayload instanceof ServiceTokenData) {
|
if (authData.authPayload instanceof ServiceTokenData) {
|
||||||
const { secretPath: serviceTkScopedSecretPath } = authData.authPayload;
|
const { scopes: tkScopes } = authData.authPayload;
|
||||||
if (secretPath !== serviceTkScopedSecretPath) {
|
const validScope = tkScopes.find(
|
||||||
|
(scope) =>
|
||||||
|
picomatch.isMatch(secretPath, scope.secretPath, { strictSlashes: false }) &&
|
||||||
|
scope.environment === environment
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!validScope) {
|
||||||
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const folderId = await getFolderIdFromServiceToken(
|
const folderId = await getFolderIdFromServiceToken(workspaceId, environment, secretPath);
|
||||||
workspaceId,
|
|
||||||
environment,
|
|
||||||
secretPath
|
|
||||||
);
|
|
||||||
|
|
||||||
const exists = await Secret.exists({
|
const exists = await Secret.exists({
|
||||||
secretBlindIndex,
|
secretBlindIndex,
|
||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
folder: folderId,
|
folder: folderId,
|
||||||
type,
|
type,
|
||||||
...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}),
|
...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {})
|
||||||
});
|
});
|
||||||
|
|
||||||
if (exists)
|
if (exists)
|
||||||
throw BadRequestError({
|
throw BadRequestError({
|
||||||
message: "Failed to create secret that already exists",
|
message: "Failed to create secret that already exists"
|
||||||
});
|
});
|
||||||
|
|
||||||
if (type === SECRET_PERSONAL) {
|
if (type === SECRET_PERSONAL) {
|
||||||
@@ -365,13 +340,12 @@ export const createSecretHelper = async ({
|
|||||||
secretBlindIndex,
|
secretBlindIndex,
|
||||||
folder: folderId,
|
folder: folderId,
|
||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
type: SECRET_SHARED,
|
type: SECRET_SHARED
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!exists)
|
if (!exists)
|
||||||
throw BadRequestError({
|
throw BadRequestError({
|
||||||
message:
|
message: "Failed to create personal secret override for no corresponding shared secret"
|
||||||
"Failed to create personal secret override for no corresponding shared secret",
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,7 +368,7 @@ export const createSecretHelper = async ({
|
|||||||
secretCommentTag,
|
secretCommentTag,
|
||||||
folder: folderId,
|
folder: folderId,
|
||||||
algorithm: ALGORITHM_AES_256_GCM,
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
keyEncoding: ENCODING_SCHEME_UTF8,
|
keyEncoding: ENCODING_SCHEME_UTF8
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
const secretVersion = new SecretVersion({
|
const secretVersion = new SecretVersion({
|
||||||
@@ -414,12 +388,12 @@ export const createSecretHelper = async ({
|
|||||||
secretValueIV,
|
secretValueIV,
|
||||||
secretValueTag,
|
secretValueTag,
|
||||||
algorithm: ALGORITHM_AES_256_GCM,
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
keyEncoding: ENCODING_SCHEME_UTF8,
|
keyEncoding: ENCODING_SCHEME_UTF8
|
||||||
});
|
});
|
||||||
|
|
||||||
// (EE) add version for new secret
|
// (EE) add version for new secret
|
||||||
await EESecretService.addSecretVersions({
|
await EESecretService.addSecretVersions({
|
||||||
secretVersions: [secretVersion],
|
secretVersions: [secretVersion]
|
||||||
});
|
});
|
||||||
|
|
||||||
// (EE) create (audit) log
|
// (EE) create (audit) log
|
||||||
@@ -427,7 +401,7 @@ export const createSecretHelper = async ({
|
|||||||
name: ACTION_ADD_SECRETS,
|
name: ACTION_ADD_SECRETS,
|
||||||
...getAuthDataPayloadIdObj(authData),
|
...getAuthDataPayloadIdObj(authData),
|
||||||
workspaceId,
|
workspaceId,
|
||||||
secretIds: [secret._id],
|
secretIds: [secret._id]
|
||||||
});
|
});
|
||||||
|
|
||||||
action &&
|
action &&
|
||||||
@@ -436,14 +410,14 @@ export const createSecretHelper = async ({
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
actions: [action],
|
actions: [action],
|
||||||
channel: authData.authChannel,
|
channel: authData.authChannel,
|
||||||
ipAddress: authData.authIP,
|
ipAddress: authData.authIP
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// (EE) take a secret snapshot
|
// (EE) take a secret snapshot
|
||||||
await EESecretService.takeSecretSnapshot({
|
await EESecretService.takeSecretSnapshot({
|
||||||
workspaceId,
|
workspaceId,
|
||||||
environment,
|
environment,
|
||||||
folderId,
|
folderId
|
||||||
});
|
});
|
||||||
|
|
||||||
const postHogClient = await TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
@@ -452,7 +426,7 @@ export const createSecretHelper = async ({
|
|||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: "secrets added",
|
event: "secrets added",
|
||||||
distinctId: await TelemetryService.getDistinctId({
|
distinctId: await TelemetryService.getDistinctId({
|
||||||
authData,
|
authData
|
||||||
}),
|
}),
|
||||||
properties: {
|
properties: {
|
||||||
numberOfSecrets: 1,
|
numberOfSecrets: 1,
|
||||||
@@ -460,8 +434,8 @@ export const createSecretHelper = async ({
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
folderId,
|
folderId,
|
||||||
channel: authData.authChannel,
|
channel: authData.authChannel,
|
||||||
userAgent: authData.authUserAgent,
|
userAgent: authData.authUserAgent
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -480,21 +454,23 @@ export const getSecretsHelper = async ({
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
environment,
|
environment,
|
||||||
authData,
|
authData,
|
||||||
secretPath = "/",
|
secretPath = "/"
|
||||||
}: GetSecretsParams) => {
|
}: GetSecretsParams) => {
|
||||||
let secrets: ISecret[] = [];
|
let secrets: ISecret[] = [];
|
||||||
// if using service token filter towards the folderId by secretpath
|
// if using service token filter towards the folderId by secretpath
|
||||||
if (authData.authPayload instanceof ServiceTokenData) {
|
if (authData.authPayload instanceof ServiceTokenData) {
|
||||||
const { secretPath: serviceTkScopedSecretPath } = authData.authPayload;
|
const { scopes: tkScopes } = authData.authPayload;
|
||||||
if (secretPath !== serviceTkScopedSecretPath) {
|
const validScope = tkScopes.find(
|
||||||
|
(scope) =>
|
||||||
|
picomatch.isMatch(secretPath, scope.secretPath, { strictSlashes: false }) &&
|
||||||
|
scope.environment === environment
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!validScope) {
|
||||||
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const folderId = await getFolderIdFromServiceToken(
|
const folderId = await getFolderIdFromServiceToken(workspaceId, environment, secretPath);
|
||||||
workspaceId,
|
|
||||||
environment,
|
|
||||||
secretPath
|
|
||||||
);
|
|
||||||
|
|
||||||
// get personal secrets first
|
// get personal secrets first
|
||||||
secrets = await Secret.find({
|
secrets = await Secret.find({
|
||||||
@@ -502,8 +478,10 @@ export const getSecretsHelper = async ({
|
|||||||
environment,
|
environment,
|
||||||
folder: folderId,
|
folder: folderId,
|
||||||
type: SECRET_PERSONAL,
|
type: SECRET_PERSONAL,
|
||||||
...getAuthDataPayloadUserObj(authData),
|
...getAuthDataPayloadUserObj(authData)
|
||||||
}).populate("tags").lean();
|
})
|
||||||
|
.populate("tags")
|
||||||
|
.lean();
|
||||||
|
|
||||||
// concat with shared secrets
|
// concat with shared secrets
|
||||||
secrets = secrets.concat(
|
secrets = secrets.concat(
|
||||||
@@ -513,9 +491,11 @@ export const getSecretsHelper = async ({
|
|||||||
folder: folderId,
|
folder: folderId,
|
||||||
type: SECRET_SHARED,
|
type: SECRET_SHARED,
|
||||||
secretBlindIndex: {
|
secretBlindIndex: {
|
||||||
$nin: secrets.map((secret) => secret.secretBlindIndex),
|
$nin: secrets.map((secret) => secret.secretBlindIndex)
|
||||||
},
|
}
|
||||||
}).populate("tags").lean()
|
})
|
||||||
|
.populate("tags")
|
||||||
|
.lean()
|
||||||
);
|
);
|
||||||
|
|
||||||
// (EE) create (audit) log
|
// (EE) create (audit) log
|
||||||
@@ -523,7 +503,7 @@ export const getSecretsHelper = async ({
|
|||||||
name: ACTION_READ_SECRETS,
|
name: ACTION_READ_SECRETS,
|
||||||
...getAuthDataPayloadIdObj(authData),
|
...getAuthDataPayloadIdObj(authData),
|
||||||
workspaceId,
|
workspaceId,
|
||||||
secretIds: secrets.map((secret) => secret._id),
|
secretIds: secrets.map((secret) => secret._id)
|
||||||
});
|
});
|
||||||
|
|
||||||
action &&
|
action &&
|
||||||
@@ -532,7 +512,7 @@ export const getSecretsHelper = async ({
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
actions: [action],
|
actions: [action],
|
||||||
channel: authData.authChannel,
|
channel: authData.authChannel,
|
||||||
ipAddress: authData.authIP,
|
ipAddress: authData.authIP
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const postHogClient = await TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
@@ -541,7 +521,7 @@ export const getSecretsHelper = async ({
|
|||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: "secrets pulled",
|
event: "secrets pulled",
|
||||||
distinctId: await TelemetryService.getDistinctId({
|
distinctId: await TelemetryService.getDistinctId({
|
||||||
authData,
|
authData
|
||||||
}),
|
}),
|
||||||
properties: {
|
properties: {
|
||||||
numberOfSecrets: secrets.length,
|
numberOfSecrets: secrets.length,
|
||||||
@@ -549,8 +529,8 @@ export const getSecretsHelper = async ({
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
folderId,
|
folderId,
|
||||||
channel: authData.authChannel,
|
channel: authData.authChannel,
|
||||||
userAgent: authData.authUserAgent,
|
userAgent: authData.authUserAgent
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -573,25 +553,27 @@ export const getSecretHelper = async ({
|
|||||||
environment,
|
environment,
|
||||||
type,
|
type,
|
||||||
authData,
|
authData,
|
||||||
secretPath = "/",
|
secretPath = "/"
|
||||||
}: GetSecretParams) => {
|
}: GetSecretParams) => {
|
||||||
const secretBlindIndex = await generateSecretBlindIndexHelper({
|
const secretBlindIndex = await generateSecretBlindIndexHelper({
|
||||||
secretName,
|
secretName,
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId)
|
||||||
});
|
});
|
||||||
let secret: ISecret | null = null;
|
let secret: ISecret | null = null;
|
||||||
// if using service token filter towards the folderId by secretpath
|
// if using service token filter towards the folderId by secretpath
|
||||||
if (authData.authPayload instanceof ServiceTokenData) {
|
if (authData.authPayload instanceof ServiceTokenData) {
|
||||||
const { secretPath: serviceTkScopedSecretPath } = authData.authPayload;
|
const { scopes: tkScopes } = authData.authPayload;
|
||||||
if (secretPath !== serviceTkScopedSecretPath) {
|
const validScope = tkScopes.find(
|
||||||
|
(scope) =>
|
||||||
|
picomatch.isMatch(secretPath, scope.secretPath, { strictSlashes: false }) &&
|
||||||
|
scope.environment === environment
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!validScope) {
|
||||||
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const folderId = await getFolderIdFromServiceToken(
|
const folderId = await getFolderIdFromServiceToken(workspaceId, environment, secretPath);
|
||||||
workspaceId,
|
|
||||||
environment,
|
|
||||||
secretPath
|
|
||||||
);
|
|
||||||
|
|
||||||
// try getting personal secret first (if exists)
|
// try getting personal secret first (if exists)
|
||||||
secret = await Secret.findOne({
|
secret = await Secret.findOne({
|
||||||
@@ -600,7 +582,7 @@ export const getSecretHelper = async ({
|
|||||||
environment,
|
environment,
|
||||||
folder: folderId,
|
folder: folderId,
|
||||||
type: type ?? SECRET_PERSONAL,
|
type: type ?? SECRET_PERSONAL,
|
||||||
...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}),
|
...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {})
|
||||||
}).lean();
|
}).lean();
|
||||||
|
|
||||||
if (!secret) {
|
if (!secret) {
|
||||||
@@ -611,7 +593,7 @@ export const getSecretHelper = async ({
|
|||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
folder: folderId,
|
folder: folderId,
|
||||||
type: SECRET_SHARED,
|
type: SECRET_SHARED
|
||||||
}).lean();
|
}).lean();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -622,7 +604,7 @@ export const getSecretHelper = async ({
|
|||||||
name: ACTION_READ_SECRETS,
|
name: ACTION_READ_SECRETS,
|
||||||
...getAuthDataPayloadIdObj(authData),
|
...getAuthDataPayloadIdObj(authData),
|
||||||
workspaceId,
|
workspaceId,
|
||||||
secretIds: [secret._id],
|
secretIds: [secret._id]
|
||||||
});
|
});
|
||||||
|
|
||||||
action &&
|
action &&
|
||||||
@@ -631,7 +613,7 @@ export const getSecretHelper = async ({
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
actions: [action],
|
actions: [action],
|
||||||
channel: authData.authChannel,
|
channel: authData.authChannel,
|
||||||
ipAddress: authData.authIP,
|
ipAddress: authData.authIP
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const postHogClient = await TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
@@ -640,7 +622,7 @@ export const getSecretHelper = async ({
|
|||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: "secrets pull",
|
event: "secrets pull",
|
||||||
distinctId: await TelemetryService.getDistinctId({
|
distinctId: await TelemetryService.getDistinctId({
|
||||||
authData,
|
authData
|
||||||
}),
|
}),
|
||||||
properties: {
|
properties: {
|
||||||
numberOfSecrets: 1,
|
numberOfSecrets: 1,
|
||||||
@@ -648,8 +630,8 @@ export const getSecretHelper = async ({
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
folderId,
|
folderId,
|
||||||
channel: authData.authChannel,
|
channel: authData.authChannel,
|
||||||
userAgent: authData.authUserAgent,
|
userAgent: authData.authUserAgent
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -679,26 +661,28 @@ export const updateSecretHelper = async ({
|
|||||||
secretValueCiphertext,
|
secretValueCiphertext,
|
||||||
secretValueIV,
|
secretValueIV,
|
||||||
secretValueTag,
|
secretValueTag,
|
||||||
secretPath,
|
secretPath
|
||||||
}: UpdateSecretParams) => {
|
}: UpdateSecretParams) => {
|
||||||
const secretBlindIndex = await generateSecretBlindIndexHelper({
|
const secretBlindIndex = await generateSecretBlindIndexHelper({
|
||||||
secretName,
|
secretName,
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId)
|
||||||
});
|
});
|
||||||
|
|
||||||
let secret: ISecret | null = null;
|
let secret: ISecret | null = null;
|
||||||
// if using service token filter towards the folderId by secretpath
|
// if using service token filter towards the folderId by secretpath
|
||||||
if (authData.authPayload instanceof ServiceTokenData) {
|
if (authData.authPayload instanceof ServiceTokenData) {
|
||||||
const { secretPath: serviceTkScopedSecretPath } = authData.authPayload;
|
const { scopes: tkScopes } = authData.authPayload;
|
||||||
if (secretPath !== serviceTkScopedSecretPath) {
|
const validScope = tkScopes.find(
|
||||||
|
(scope) =>
|
||||||
|
picomatch.isMatch(secretPath, scope.secretPath, { strictSlashes: false }) &&
|
||||||
|
scope.environment === environment
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!validScope) {
|
||||||
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const folderId = await getFolderIdFromServiceToken(
|
const folderId = await getFolderIdFromServiceToken(workspaceId, environment, secretPath);
|
||||||
workspaceId,
|
|
||||||
environment,
|
|
||||||
secretPath
|
|
||||||
);
|
|
||||||
|
|
||||||
if (type === SECRET_SHARED) {
|
if (type === SECRET_SHARED) {
|
||||||
// case: update shared secret
|
// case: update shared secret
|
||||||
@@ -708,16 +692,16 @@ export const updateSecretHelper = async ({
|
|||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
folder: folderId,
|
folder: folderId,
|
||||||
type,
|
type
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
secretValueCiphertext,
|
secretValueCiphertext,
|
||||||
secretValueIV,
|
secretValueIV,
|
||||||
secretValueTag,
|
secretValueTag,
|
||||||
$inc: { version: 1 },
|
$inc: { version: 1 }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
new: true,
|
new: true
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -730,16 +714,16 @@ export const updateSecretHelper = async ({
|
|||||||
environment,
|
environment,
|
||||||
type,
|
type,
|
||||||
folder: folderId,
|
folder: folderId,
|
||||||
...getAuthDataPayloadUserObj(authData),
|
...getAuthDataPayloadUserObj(authData)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
secretValueCiphertext,
|
secretValueCiphertext,
|
||||||
secretValueIV,
|
secretValueIV,
|
||||||
secretValueTag,
|
secretValueTag,
|
||||||
$inc: { version: 1 },
|
$inc: { version: 1 }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
new: true,
|
new: true
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -763,12 +747,12 @@ export const updateSecretHelper = async ({
|
|||||||
secretValueIV,
|
secretValueIV,
|
||||||
secretValueTag,
|
secretValueTag,
|
||||||
algorithm: ALGORITHM_AES_256_GCM,
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
keyEncoding: ENCODING_SCHEME_UTF8,
|
keyEncoding: ENCODING_SCHEME_UTF8
|
||||||
});
|
});
|
||||||
|
|
||||||
// (EE) add version for new secret
|
// (EE) add version for new secret
|
||||||
await EESecretService.addSecretVersions({
|
await EESecretService.addSecretVersions({
|
||||||
secretVersions: [secretVersion],
|
secretVersions: [secretVersion]
|
||||||
});
|
});
|
||||||
|
|
||||||
// (EE) create (audit) log
|
// (EE) create (audit) log
|
||||||
@@ -776,7 +760,7 @@ export const updateSecretHelper = async ({
|
|||||||
name: ACTION_UPDATE_SECRETS,
|
name: ACTION_UPDATE_SECRETS,
|
||||||
...getAuthDataPayloadIdObj(authData),
|
...getAuthDataPayloadIdObj(authData),
|
||||||
workspaceId,
|
workspaceId,
|
||||||
secretIds: [secret._id],
|
secretIds: [secret._id]
|
||||||
});
|
});
|
||||||
|
|
||||||
action &&
|
action &&
|
||||||
@@ -785,14 +769,14 @@ export const updateSecretHelper = async ({
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
actions: [action],
|
actions: [action],
|
||||||
channel: authData.authChannel,
|
channel: authData.authChannel,
|
||||||
ipAddress: authData.authIP,
|
ipAddress: authData.authIP
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// (EE) take a secret snapshot
|
// (EE) take a secret snapshot
|
||||||
await EESecretService.takeSecretSnapshot({
|
await EESecretService.takeSecretSnapshot({
|
||||||
workspaceId,
|
workspaceId,
|
||||||
environment,
|
environment,
|
||||||
folderId: secret?.folder,
|
folderId: secret?.folder
|
||||||
});
|
});
|
||||||
|
|
||||||
const postHogClient = await TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
@@ -801,7 +785,7 @@ export const updateSecretHelper = async ({
|
|||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: "secrets modified",
|
event: "secrets modified",
|
||||||
distinctId: await TelemetryService.getDistinctId({
|
distinctId: await TelemetryService.getDistinctId({
|
||||||
authData,
|
authData
|
||||||
}),
|
}),
|
||||||
properties: {
|
properties: {
|
||||||
numberOfSecrets: 1,
|
numberOfSecrets: 1,
|
||||||
@@ -809,8 +793,8 @@ export const updateSecretHelper = async ({
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
folderId,
|
folderId,
|
||||||
channel: authData.authChannel,
|
channel: authData.authChannel,
|
||||||
userAgent: authData.authUserAgent,
|
userAgent: authData.authUserAgent
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -833,26 +817,27 @@ export const deleteSecretHelper = async ({
|
|||||||
environment,
|
environment,
|
||||||
type,
|
type,
|
||||||
authData,
|
authData,
|
||||||
secretPath = "/",
|
secretPath = "/"
|
||||||
}: DeleteSecretParams) => {
|
}: DeleteSecretParams) => {
|
||||||
const secretBlindIndex = await generateSecretBlindIndexHelper({
|
const secretBlindIndex = await generateSecretBlindIndexHelper({
|
||||||
secretName,
|
secretName,
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId)
|
||||||
});
|
});
|
||||||
|
|
||||||
// if using service token filter towards the folderId by secretpath
|
// if using service token filter towards the folderId by secretpath
|
||||||
if (authData.authPayload instanceof ServiceTokenData) {
|
if (authData.authPayload instanceof ServiceTokenData) {
|
||||||
const { secretPath: serviceTkScopedSecretPath } = authData.authPayload;
|
const { scopes: tkScopes } = authData.authPayload;
|
||||||
|
const validScope = tkScopes.find(
|
||||||
|
(scope) =>
|
||||||
|
picomatch.isMatch(secretPath, scope.secretPath, { strictSlashes: false }) &&
|
||||||
|
scope.environment === environment
|
||||||
|
);
|
||||||
|
|
||||||
if (secretPath !== serviceTkScopedSecretPath) {
|
if (!validScope) {
|
||||||
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const folderId = await getFolderIdFromServiceToken(
|
const folderId = await getFolderIdFromServiceToken(workspaceId, environment, secretPath);
|
||||||
workspaceId,
|
|
||||||
environment,
|
|
||||||
secretPath
|
|
||||||
);
|
|
||||||
|
|
||||||
let secrets: ISecret[] = [];
|
let secrets: ISecret[] = [];
|
||||||
let secret: ISecret | null = null;
|
let secret: ISecret | null = null;
|
||||||
@@ -862,7 +847,7 @@ export const deleteSecretHelper = async ({
|
|||||||
secretBlindIndex,
|
secretBlindIndex,
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
folder: folderId,
|
folder: folderId
|
||||||
}).lean();
|
}).lean();
|
||||||
|
|
||||||
secret = await Secret.findOneAndDelete({
|
secret = await Secret.findOneAndDelete({
|
||||||
@@ -870,14 +855,14 @@ export const deleteSecretHelper = async ({
|
|||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
type,
|
type,
|
||||||
folder: folderId,
|
folder: folderId
|
||||||
}).lean();
|
}).lean();
|
||||||
|
|
||||||
await Secret.deleteMany({
|
await Secret.deleteMany({
|
||||||
secretBlindIndex,
|
secretBlindIndex,
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
folder: folderId,
|
folder: folderId
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
secret = await Secret.findOneAndDelete({
|
secret = await Secret.findOneAndDelete({
|
||||||
@@ -886,7 +871,7 @@ export const deleteSecretHelper = async ({
|
|||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
type,
|
type,
|
||||||
...getAuthDataPayloadUserObj(authData),
|
...getAuthDataPayloadUserObj(authData)
|
||||||
}).lean();
|
}).lean();
|
||||||
|
|
||||||
if (secret) {
|
if (secret) {
|
||||||
@@ -897,7 +882,7 @@ export const deleteSecretHelper = async ({
|
|||||||
if (!secret) throw SecretNotFoundError();
|
if (!secret) throw SecretNotFoundError();
|
||||||
|
|
||||||
await EESecretService.markDeletedSecretVersions({
|
await EESecretService.markDeletedSecretVersions({
|
||||||
secretIds: secrets.map((secret) => secret._id),
|
secretIds: secrets.map((secret) => secret._id)
|
||||||
});
|
});
|
||||||
|
|
||||||
// (EE) create (audit) log
|
// (EE) create (audit) log
|
||||||
@@ -905,22 +890,23 @@ export const deleteSecretHelper = async ({
|
|||||||
name: ACTION_DELETE_SECRETS,
|
name: ACTION_DELETE_SECRETS,
|
||||||
...getAuthDataPayloadIdObj(authData),
|
...getAuthDataPayloadIdObj(authData),
|
||||||
workspaceId,
|
workspaceId,
|
||||||
secretIds: secrets.map((secret) => secret._id),
|
secretIds: secrets.map((secret) => secret._id)
|
||||||
});
|
});
|
||||||
|
|
||||||
action && (await EELogService.createLog({
|
action &&
|
||||||
...getAuthDataPayloadIdObj(authData),
|
(await EELogService.createLog({
|
||||||
workspaceId,
|
...getAuthDataPayloadIdObj(authData),
|
||||||
actions: [action],
|
workspaceId,
|
||||||
channel: authData.authChannel,
|
actions: [action],
|
||||||
ipAddress: authData.authIP,
|
channel: authData.authChannel,
|
||||||
}));
|
ipAddress: authData.authIP
|
||||||
|
}));
|
||||||
|
|
||||||
// (EE) take a secret snapshot
|
// (EE) take a secret snapshot
|
||||||
await EESecretService.takeSecretSnapshot({
|
await EESecretService.takeSecretSnapshot({
|
||||||
workspaceId,
|
workspaceId,
|
||||||
environment,
|
environment,
|
||||||
folderId: secret?.folder,
|
folderId: secret?.folder
|
||||||
});
|
});
|
||||||
|
|
||||||
const postHogClient = await TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
@@ -929,7 +915,7 @@ export const deleteSecretHelper = async ({
|
|||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: "secrets deleted",
|
event: "secrets deleted",
|
||||||
distinctId: await TelemetryService.getDistinctId({
|
distinctId: await TelemetryService.getDistinctId({
|
||||||
authData,
|
authData
|
||||||
}),
|
}),
|
||||||
properties: {
|
properties: {
|
||||||
numberOfSecrets: secrets.length,
|
numberOfSecrets: secrets.length,
|
||||||
@@ -937,13 +923,13 @@ export const deleteSecretHelper = async ({
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
folderId,
|
folderId,
|
||||||
channel: authData.authChannel,
|
channel: authData.authChannel,
|
||||||
userAgent: authData.authUserAgent,
|
userAgent: authData.authUserAgent
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return ({
|
return {
|
||||||
secrets,
|
secrets,
|
||||||
secret,
|
secret
|
||||||
});
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ export interface IServiceTokenData extends Document {
|
|||||||
_id: Types.ObjectId;
|
_id: Types.ObjectId;
|
||||||
name: string;
|
name: string;
|
||||||
workspace: Types.ObjectId;
|
workspace: Types.ObjectId;
|
||||||
environment: string;
|
scopes: Array<{
|
||||||
|
environment: string;
|
||||||
|
secretPath: string;
|
||||||
|
}>;
|
||||||
user: Types.ObjectId;
|
user: Types.ObjectId;
|
||||||
serviceAccount: Types.ObjectId;
|
serviceAccount: Types.ObjectId;
|
||||||
lastUsed: Date;
|
lastUsed: Date;
|
||||||
@@ -13,7 +16,6 @@ export interface IServiceTokenData extends Document {
|
|||||||
encryptedKey: string;
|
encryptedKey: string;
|
||||||
iv: string;
|
iv: string;
|
||||||
tag: string;
|
tag: string;
|
||||||
secretPath: string;
|
|
||||||
permissions: string[];
|
permissions: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,68 +23,72 @@ const serviceTokenDataSchema = new Schema<IServiceTokenData>(
|
|||||||
{
|
{
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true
|
||||||
},
|
},
|
||||||
workspace: {
|
workspace: {
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: "Workspace",
|
ref: "Workspace",
|
||||||
required: true,
|
required: true
|
||||||
},
|
},
|
||||||
environment: {
|
scopes: {
|
||||||
type: String,
|
type: [
|
||||||
required: true,
|
{
|
||||||
|
environment: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
secretPath: {
|
||||||
|
type: String,
|
||||||
|
default: "/",
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
required: true
|
||||||
},
|
},
|
||||||
user: {
|
user: {
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: "User",
|
ref: "User",
|
||||||
required: true,
|
required: true
|
||||||
},
|
},
|
||||||
serviceAccount: {
|
serviceAccount: {
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: "ServiceAccount",
|
ref: "ServiceAccount"
|
||||||
},
|
},
|
||||||
lastUsed: {
|
lastUsed: {
|
||||||
type: Date,
|
type: Date
|
||||||
},
|
},
|
||||||
expiresAt: {
|
expiresAt: {
|
||||||
type: Date,
|
type: Date
|
||||||
},
|
},
|
||||||
secretHash: {
|
secretHash: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
select: false,
|
select: false
|
||||||
},
|
},
|
||||||
encryptedKey: {
|
encryptedKey: {
|
||||||
type: String,
|
type: String,
|
||||||
select: false,
|
select: false
|
||||||
},
|
},
|
||||||
iv: {
|
iv: {
|
||||||
type: String,
|
type: String,
|
||||||
select: false,
|
select: false
|
||||||
},
|
},
|
||||||
tag: {
|
tag: {
|
||||||
type: String,
|
type: String,
|
||||||
select: false,
|
select: false
|
||||||
},
|
},
|
||||||
permissions: {
|
permissions: {
|
||||||
type: [String],
|
type: [String],
|
||||||
enum: ["read", "write"],
|
enum: ["read", "write"],
|
||||||
default: ["read"],
|
default: ["read"]
|
||||||
},
|
}
|
||||||
secretPath: {
|
|
||||||
type: String,
|
|
||||||
default: "/",
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
timestamps: true,
|
timestamps: true
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const ServiceTokenData = model<IServiceTokenData>(
|
const ServiceTokenData = model<IServiceTokenData>("ServiceTokenData", serviceTokenDataSchema);
|
||||||
"ServiceTokenData",
|
|
||||||
serviceTokenDataSchema
|
|
||||||
);
|
|
||||||
|
|
||||||
export default ServiceTokenData;
|
export default ServiceTokenData;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
requireAuth,
|
requireAuth,
|
||||||
requireServiceTokenDataAuth,
|
requireServiceTokenDataAuth,
|
||||||
requireWorkspaceAuth,
|
requireWorkspaceAuth,
|
||||||
validateRequest,
|
validateRequest
|
||||||
} from "../../middleware";
|
} from "../../middleware";
|
||||||
import { body, param } from "express-validator";
|
import { body, param } from "express-validator";
|
||||||
import {
|
import {
|
||||||
@@ -13,14 +13,14 @@ import {
|
|||||||
AUTH_MODE_SERVICE_ACCOUNT,
|
AUTH_MODE_SERVICE_ACCOUNT,
|
||||||
AUTH_MODE_SERVICE_TOKEN,
|
AUTH_MODE_SERVICE_TOKEN,
|
||||||
MEMBER,
|
MEMBER,
|
||||||
PERMISSION_WRITE_SECRETS,
|
PERMISSION_WRITE_SECRETS
|
||||||
} from "../../variables";
|
} from "../../variables";
|
||||||
import { serviceTokenDataController } from "../../controllers/v2";
|
import { serviceTokenDataController } from "../../controllers/v2";
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/",
|
"/",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AUTH_MODE_SERVICE_TOKEN],
|
acceptedAuthModes: [AUTH_MODE_SERVICE_TOKEN]
|
||||||
}),
|
}),
|
||||||
serviceTokenDataController.getServiceTokenData
|
serviceTokenDataController.getServiceTokenData
|
||||||
);
|
);
|
||||||
@@ -28,33 +28,30 @@ router.get(
|
|||||||
router.post(
|
router.post(
|
||||||
"/",
|
"/",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_SERVICE_ACCOUNT],
|
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_SERVICE_ACCOUNT]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
requireWorkspaceAuth({
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
locationWorkspaceId: "body",
|
locationWorkspaceId: "body",
|
||||||
locationEnvironment: "body",
|
locationEnvironment: "body",
|
||||||
requiredPermissions: [PERMISSION_WRITE_SECRETS],
|
requiredPermissions: [PERMISSION_WRITE_SECRETS]
|
||||||
}),
|
}),
|
||||||
body("name").exists().isString().trim(),
|
body("name").exists().isString().trim(),
|
||||||
body("workspaceId").exists().isString().trim(),
|
body("workspaceId").exists().isString().trim(),
|
||||||
body("environment").exists().isString().trim(),
|
body("scopes").exists().isArray(),
|
||||||
|
body("scopes.*.environment").exists().isString().trim(),
|
||||||
|
body("scopes.*.secretPath").exists().isString().trim(),
|
||||||
body("encryptedKey").exists().isString().trim(),
|
body("encryptedKey").exists().isString().trim(),
|
||||||
body("iv").exists().isString().trim(),
|
body("iv").exists().isString().trim(),
|
||||||
body("secretPath").isString().default("/").trim(),
|
|
||||||
body("tag").exists().isString().trim(),
|
body("tag").exists().isString().trim(),
|
||||||
body("expiresIn").exists().isNumeric(), // measured in ms
|
body("expiresIn").exists().isNumeric(), // measured in ms
|
||||||
body("permissions")
|
body("permissions")
|
||||||
.isArray({ min: 1 })
|
.isArray({ min: 1 })
|
||||||
.custom((value: string[]) => {
|
.custom((value: string[]) => {
|
||||||
const allowedPermissions = ["read", "write"];
|
const allowedPermissions = ["read", "write"];
|
||||||
const invalidValues = value.filter(
|
const invalidValues = value.filter((v) => !allowedPermissions.includes(v));
|
||||||
(v) => !allowedPermissions.includes(v)
|
|
||||||
);
|
|
||||||
if (invalidValues.length > 0) {
|
if (invalidValues.length > 0) {
|
||||||
throw new Error(
|
throw new Error(`permissions contains invalid values: ${invalidValues.join(", ")}`);
|
||||||
`permissions contains invalid values: ${invalidValues.join(", ")}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -66,10 +63,10 @@ router.post(
|
|||||||
router.delete(
|
router.delete(
|
||||||
"/:serviceTokenDataId",
|
"/:serviceTokenDataId",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AUTH_MODE_JWT],
|
acceptedAuthModes: [AUTH_MODE_JWT]
|
||||||
}),
|
}),
|
||||||
requireServiceTokenDataAuth({
|
requireServiceTokenDataAuth({
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
acceptedRoles: [ADMIN, MEMBER]
|
||||||
}),
|
}),
|
||||||
param("serviceTokenDataId").exists().trim(),
|
param("serviceTokenDataId").exists().trim(),
|
||||||
validateRequest,
|
validateRequest,
|
||||||
|
|||||||
@@ -13,14 +13,14 @@ import {
|
|||||||
Secret,
|
Secret,
|
||||||
SecretBlindIndexData,
|
SecretBlindIndexData,
|
||||||
ServiceTokenData,
|
ServiceTokenData,
|
||||||
Workspace,
|
Workspace
|
||||||
} from "../../models";
|
} from "../../models";
|
||||||
import { generateKeyPair } from "../../utils/crypto";
|
import { generateKeyPair } from "../../utils/crypto";
|
||||||
import { client, getEncryptionKey, getRootEncryptionKey } from "../../config";
|
import { client, getEncryptionKey, getRootEncryptionKey } from "../../config";
|
||||||
import {
|
import {
|
||||||
ALGORITHM_AES_256_GCM,
|
ALGORITHM_AES_256_GCM,
|
||||||
ENCODING_SCHEME_BASE64,
|
ENCODING_SCHEME_BASE64,
|
||||||
ENCODING_SCHEME_UTF8,
|
ENCODING_SCHEME_UTF8
|
||||||
} from "../../variables";
|
} from "../../variables";
|
||||||
import { InternalServerError } from "../errors";
|
import { InternalServerError } from "../errors";
|
||||||
|
|
||||||
@@ -29,10 +29,7 @@ import { InternalServerError } from "../errors";
|
|||||||
* corresponding secret versions
|
* corresponding secret versions
|
||||||
*/
|
*/
|
||||||
export const backfillSecretVersions = async () => {
|
export const backfillSecretVersions = async () => {
|
||||||
await Secret.updateMany(
|
await Secret.updateMany({ version: { $exists: false } }, { $set: { version: 1 } });
|
||||||
{ version: { $exists: false } },
|
|
||||||
{ $set: { version: 1 } }
|
|
||||||
);
|
|
||||||
|
|
||||||
const unversionedSecrets: ISecret[] = await Secret.aggregate([
|
const unversionedSecrets: ISecret[] = await Secret.aggregate([
|
||||||
{
|
{
|
||||||
@@ -40,14 +37,14 @@ export const backfillSecretVersions = async () => {
|
|||||||
from: "secretversions",
|
from: "secretversions",
|
||||||
localField: "_id",
|
localField: "_id",
|
||||||
foreignField: "secret",
|
foreignField: "secret",
|
||||||
as: "versions",
|
as: "versions"
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$match: {
|
$match: {
|
||||||
versions: { $size: 0 },
|
versions: { $size: 0 }
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (unversionedSecrets.length > 0) {
|
if (unversionedSecrets.length > 0) {
|
||||||
@@ -62,9 +59,9 @@ export const backfillSecretVersions = async () => {
|
|||||||
workspace: s.workspace,
|
workspace: s.workspace,
|
||||||
environment: s.environment,
|
environment: s.environment,
|
||||||
algorithm: ALGORITHM_AES_256_GCM,
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
keyEncoding: ENCODING_SCHEME_UTF8,
|
keyEncoding: ENCODING_SCHEME_UTF8
|
||||||
})
|
})
|
||||||
),
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
console.log("Migration: Secret version migration v1 complete");
|
console.log("Migration: Secret version migration v1 complete");
|
||||||
@@ -80,8 +77,8 @@ export const backfillBots = async () => {
|
|||||||
const workspaceIdsWithBot = await Bot.distinct("workspace");
|
const workspaceIdsWithBot = await Bot.distinct("workspace");
|
||||||
const workspaceIdsToAddBot = await Workspace.distinct("_id", {
|
const workspaceIdsToAddBot = await Workspace.distinct("_id", {
|
||||||
_id: {
|
_id: {
|
||||||
$nin: workspaceIdsWithBot,
|
$nin: workspaceIdsWithBot
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (workspaceIdsToAddBot.length === 0) return;
|
if (workspaceIdsToAddBot.length === 0) return;
|
||||||
@@ -94,7 +91,7 @@ export const backfillBots = async () => {
|
|||||||
const {
|
const {
|
||||||
ciphertext: encryptedPrivateKey,
|
ciphertext: encryptedPrivateKey,
|
||||||
iv,
|
iv,
|
||||||
tag,
|
tag
|
||||||
} = client.encryptSymmetric(privateKey, rootEncryptionKey);
|
} = client.encryptSymmetric(privateKey, rootEncryptionKey);
|
||||||
|
|
||||||
return new Bot({
|
return new Bot({
|
||||||
@@ -106,16 +103,16 @@ export const backfillBots = async () => {
|
|||||||
iv,
|
iv,
|
||||||
tag,
|
tag,
|
||||||
algorithm: ALGORITHM_AES_256_GCM,
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
keyEncoding: ENCODING_SCHEME_BASE64,
|
keyEncoding: ENCODING_SCHEME_BASE64
|
||||||
});
|
});
|
||||||
} else if (encryptionKey) {
|
} else if (encryptionKey) {
|
||||||
const {
|
const {
|
||||||
ciphertext: encryptedPrivateKey,
|
ciphertext: encryptedPrivateKey,
|
||||||
iv,
|
iv,
|
||||||
tag,
|
tag
|
||||||
} = encryptSymmetric128BitHexKeyUTF8({
|
} = encryptSymmetric128BitHexKeyUTF8({
|
||||||
plaintext: privateKey,
|
plaintext: privateKey,
|
||||||
key: encryptionKey,
|
key: encryptionKey
|
||||||
});
|
});
|
||||||
|
|
||||||
return new Bot({
|
return new Bot({
|
||||||
@@ -127,13 +124,12 @@ export const backfillBots = async () => {
|
|||||||
iv,
|
iv,
|
||||||
tag,
|
tag,
|
||||||
algorithm: ALGORITHM_AES_256_GCM,
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
keyEncoding: ENCODING_SCHEME_UTF8,
|
keyEncoding: ENCODING_SCHEME_UTF8
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
throw InternalServerError({
|
throw InternalServerError({
|
||||||
message:
|
message: "Failed to backfill workspace bots due to missing encryption key"
|
||||||
"Failed to backfill workspace bots due to missing encryption key",
|
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -149,13 +145,11 @@ export const backfillSecretBlindIndexData = async () => {
|
|||||||
const encryptionKey = await getEncryptionKey();
|
const encryptionKey = await getEncryptionKey();
|
||||||
const rootEncryptionKey = await getRootEncryptionKey();
|
const rootEncryptionKey = await getRootEncryptionKey();
|
||||||
|
|
||||||
const workspaceIdsBlindIndexed = await SecretBlindIndexData.distinct(
|
const workspaceIdsBlindIndexed = await SecretBlindIndexData.distinct("workspace");
|
||||||
"workspace"
|
|
||||||
);
|
|
||||||
const workspaceIdsToBlindIndex = await Workspace.distinct("_id", {
|
const workspaceIdsToBlindIndex = await Workspace.distinct("_id", {
|
||||||
_id: {
|
_id: {
|
||||||
$nin: workspaceIdsBlindIndexed,
|
$nin: workspaceIdsBlindIndexed
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (workspaceIdsToBlindIndex.length === 0) return;
|
if (workspaceIdsToBlindIndex.length === 0) return;
|
||||||
@@ -168,7 +162,7 @@ export const backfillSecretBlindIndexData = async () => {
|
|||||||
const {
|
const {
|
||||||
ciphertext: encryptedSaltCiphertext,
|
ciphertext: encryptedSaltCiphertext,
|
||||||
iv: saltIV,
|
iv: saltIV,
|
||||||
tag: saltTag,
|
tag: saltTag
|
||||||
} = client.encryptSymmetric(salt, rootEncryptionKey);
|
} = client.encryptSymmetric(salt, rootEncryptionKey);
|
||||||
|
|
||||||
return new SecretBlindIndexData({
|
return new SecretBlindIndexData({
|
||||||
@@ -177,16 +171,16 @@ export const backfillSecretBlindIndexData = async () => {
|
|||||||
saltIV,
|
saltIV,
|
||||||
saltTag,
|
saltTag,
|
||||||
algorithm: ALGORITHM_AES_256_GCM,
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
keyEncoding: ENCODING_SCHEME_BASE64,
|
keyEncoding: ENCODING_SCHEME_BASE64
|
||||||
});
|
});
|
||||||
} else if (encryptionKey) {
|
} else if (encryptionKey) {
|
||||||
const {
|
const {
|
||||||
ciphertext: encryptedSaltCiphertext,
|
ciphertext: encryptedSaltCiphertext,
|
||||||
iv: saltIV,
|
iv: saltIV,
|
||||||
tag: saltTag,
|
tag: saltTag
|
||||||
} = encryptSymmetric128BitHexKeyUTF8({
|
} = encryptSymmetric128BitHexKeyUTF8({
|
||||||
plaintext: salt,
|
plaintext: salt,
|
||||||
key: encryptionKey,
|
key: encryptionKey
|
||||||
});
|
});
|
||||||
|
|
||||||
return new SecretBlindIndexData({
|
return new SecretBlindIndexData({
|
||||||
@@ -195,13 +189,12 @@ export const backfillSecretBlindIndexData = async () => {
|
|||||||
saltIV,
|
saltIV,
|
||||||
saltTag,
|
saltTag,
|
||||||
algorithm: ALGORITHM_AES_256_GCM,
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
keyEncoding: ENCODING_SCHEME_UTF8,
|
keyEncoding: ENCODING_SCHEME_UTF8
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
throw InternalServerError({
|
throw InternalServerError({
|
||||||
message:
|
message: "Failed to backfill secret blind index data due to missing encryption key"
|
||||||
"Failed to backfill secret blind index data due to missing encryption key",
|
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -219,17 +212,17 @@ export const backfillEncryptionMetadata = async () => {
|
|||||||
await Secret.updateMany(
|
await Secret.updateMany(
|
||||||
{
|
{
|
||||||
algorithm: {
|
algorithm: {
|
||||||
$exists: false,
|
$exists: false
|
||||||
},
|
},
|
||||||
keyEncoding: {
|
keyEncoding: {
|
||||||
$exists: false,
|
$exists: false
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$set: {
|
$set: {
|
||||||
algorithm: ALGORITHM_AES_256_GCM,
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
keyEncoding: ENCODING_SCHEME_UTF8,
|
keyEncoding: ENCODING_SCHEME_UTF8
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -237,17 +230,17 @@ export const backfillEncryptionMetadata = async () => {
|
|||||||
await SecretVersion.updateMany(
|
await SecretVersion.updateMany(
|
||||||
{
|
{
|
||||||
algorithm: {
|
algorithm: {
|
||||||
$exists: false,
|
$exists: false
|
||||||
},
|
},
|
||||||
keyEncoding: {
|
keyEncoding: {
|
||||||
$exists: false,
|
$exists: false
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$set: {
|
$set: {
|
||||||
algorithm: ALGORITHM_AES_256_GCM,
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
keyEncoding: ENCODING_SCHEME_UTF8,
|
keyEncoding: ENCODING_SCHEME_UTF8
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -255,17 +248,17 @@ export const backfillEncryptionMetadata = async () => {
|
|||||||
await SecretBlindIndexData.updateMany(
|
await SecretBlindIndexData.updateMany(
|
||||||
{
|
{
|
||||||
algorithm: {
|
algorithm: {
|
||||||
$exists: false,
|
$exists: false
|
||||||
},
|
},
|
||||||
keyEncoding: {
|
keyEncoding: {
|
||||||
$exists: false,
|
$exists: false
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$set: {
|
$set: {
|
||||||
algorithm: ALGORITHM_AES_256_GCM,
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
keyEncoding: ENCODING_SCHEME_UTF8,
|
keyEncoding: ENCODING_SCHEME_UTF8
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -273,17 +266,17 @@ export const backfillEncryptionMetadata = async () => {
|
|||||||
await Bot.updateMany(
|
await Bot.updateMany(
|
||||||
{
|
{
|
||||||
algorithm: {
|
algorithm: {
|
||||||
$exists: false,
|
$exists: false
|
||||||
},
|
},
|
||||||
keyEncoding: {
|
keyEncoding: {
|
||||||
$exists: false,
|
$exists: false
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$set: {
|
$set: {
|
||||||
algorithm: ALGORITHM_AES_256_GCM,
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
keyEncoding: ENCODING_SCHEME_UTF8,
|
keyEncoding: ENCODING_SCHEME_UTF8
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -291,17 +284,17 @@ export const backfillEncryptionMetadata = async () => {
|
|||||||
await BackupPrivateKey.updateMany(
|
await BackupPrivateKey.updateMany(
|
||||||
{
|
{
|
||||||
algorithm: {
|
algorithm: {
|
||||||
$exists: false,
|
$exists: false
|
||||||
},
|
},
|
||||||
keyEncoding: {
|
keyEncoding: {
|
||||||
$exists: false,
|
$exists: false
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$set: {
|
$set: {
|
||||||
algorithm: ALGORITHM_AES_256_GCM,
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
keyEncoding: ENCODING_SCHEME_UTF8,
|
keyEncoding: ENCODING_SCHEME_UTF8
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -309,17 +302,17 @@ export const backfillEncryptionMetadata = async () => {
|
|||||||
await IntegrationAuth.updateMany(
|
await IntegrationAuth.updateMany(
|
||||||
{
|
{
|
||||||
algorithm: {
|
algorithm: {
|
||||||
$exists: false,
|
$exists: false
|
||||||
},
|
},
|
||||||
keyEncoding: {
|
keyEncoding: {
|
||||||
$exists: false,
|
$exists: false
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$set: {
|
$set: {
|
||||||
algorithm: ALGORITHM_AES_256_GCM,
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
keyEncoding: ENCODING_SCHEME_UTF8,
|
keyEncoding: ENCODING_SCHEME_UTF8
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -328,26 +321,26 @@ export const backfillSecretFolders = async () => {
|
|||||||
await Secret.updateMany(
|
await Secret.updateMany(
|
||||||
{
|
{
|
||||||
folder: {
|
folder: {
|
||||||
$exists: false,
|
$exists: false
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$set: {
|
$set: {
|
||||||
folder: "root",
|
folder: "root"
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
await SecretVersion.updateMany(
|
await SecretVersion.updateMany(
|
||||||
{
|
{
|
||||||
folder: {
|
folder: {
|
||||||
$exists: false,
|
$exists: false
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$set: {
|
$set: {
|
||||||
folder: "root",
|
folder: "root"
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -355,20 +348,20 @@ export const backfillSecretFolders = async () => {
|
|||||||
await SecretVersion.updateMany(
|
await SecretVersion.updateMany(
|
||||||
{
|
{
|
||||||
tags: {
|
tags: {
|
||||||
$exists: false,
|
$exists: false
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$set: {
|
$set: {
|
||||||
tags: [],
|
tags: []
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
let secretSnapshots = await SecretSnapshot.find({
|
let secretSnapshots = await SecretSnapshot.find({
|
||||||
environment: {
|
environment: {
|
||||||
$exists: false,
|
$exists: false
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
.populate<{ secretVersions: ISecretVersion[] }>("secretVersions")
|
.populate<{ secretVersions: ISecretVersion[] }>("secretVersions")
|
||||||
.limit(50);
|
.limit(50);
|
||||||
@@ -377,8 +370,7 @@ export const backfillSecretFolders = async () => {
|
|||||||
for (const secSnapshot of secretSnapshots) {
|
for (const secSnapshot of secretSnapshots) {
|
||||||
const groupSnapByEnv: Record<string, Array<ISecretVersion>> = {};
|
const groupSnapByEnv: Record<string, Array<ISecretVersion>> = {};
|
||||||
secSnapshot.secretVersions.forEach((secVer) => {
|
secSnapshot.secretVersions.forEach((secVer) => {
|
||||||
if (!groupSnapByEnv?.[secVer.environment])
|
if (!groupSnapByEnv?.[secVer.environment]) groupSnapByEnv[secVer.environment] = [];
|
||||||
groupSnapByEnv[secVer.environment] = [];
|
|
||||||
groupSnapByEnv[secVer.environment].push(secVer);
|
groupSnapByEnv[secVer.environment].push(secVer);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -390,7 +382,7 @@ export const backfillSecretFolders = async () => {
|
|||||||
...secSnapshot.toObject({ virtuals: false }),
|
...secSnapshot.toObject({ virtuals: false }),
|
||||||
_id: new Types.ObjectId(),
|
_id: new Types.ObjectId(),
|
||||||
environment: snapEnv,
|
environment: snapEnv,
|
||||||
secretVersions: secretIdsOfEnvGroup,
|
secretVersions: secretIdsOfEnvGroup
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -400,8 +392,8 @@ export const backfillSecretFolders = async () => {
|
|||||||
|
|
||||||
secretSnapshots = await SecretSnapshot.find({
|
secretSnapshots = await SecretSnapshot.find({
|
||||||
environment: {
|
environment: {
|
||||||
$exists: false,
|
$exists: false
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
.populate<{ secretVersions: ISecretVersion[] }>("secretVersions")
|
.populate<{ secretVersions: ISecretVersion[] }>("secretVersions")
|
||||||
.limit(50);
|
.limit(50);
|
||||||
@@ -414,13 +406,13 @@ export const backfillServiceToken = async () => {
|
|||||||
await ServiceTokenData.updateMany(
|
await ServiceTokenData.updateMany(
|
||||||
{
|
{
|
||||||
secretPath: {
|
secretPath: {
|
||||||
$exists: false,
|
$exists: false
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$set: {
|
$set: {
|
||||||
secretPath: "/",
|
secretPath: "/"
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
console.log("Migration: Service token migration v1 complete");
|
console.log("Migration: Service token migration v1 complete");
|
||||||
@@ -430,14 +422,36 @@ export const backfillIntegration = async () => {
|
|||||||
await Integration.updateMany(
|
await Integration.updateMany(
|
||||||
{
|
{
|
||||||
secretPath: {
|
secretPath: {
|
||||||
$exists: false,
|
$exists: false
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$set: {
|
$set: {
|
||||||
secretPath: "/",
|
secretPath: "/"
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
console.log("Migration: Integration migration v1 complete");
|
console.log("Migration: Integration migration v1 complete");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const backfillServiceTokenMultiScope = async () => {
|
||||||
|
await ServiceTokenData.updateMany(
|
||||||
|
{
|
||||||
|
scopes: {
|
||||||
|
$exists: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
{
|
||||||
|
$set: {
|
||||||
|
scopes: [{ environment: "$environment", secretPath: "$secretPath" }]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$unset: ["environment", "secretPath"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("Migration: Service token migration v2 complete");
|
||||||
|
};
|
||||||
|
|||||||
@@ -14,17 +14,15 @@ import {
|
|||||||
backfillSecretFolders,
|
backfillSecretFolders,
|
||||||
backfillSecretVersions,
|
backfillSecretVersions,
|
||||||
backfillServiceToken,
|
backfillServiceToken,
|
||||||
|
backfillServiceTokenMultiScope
|
||||||
} from "./backfillData";
|
} from "./backfillData";
|
||||||
import {
|
import { reencryptBotPrivateKeys, reencryptSecretBlindIndexDataSalts } from "./reencryptData";
|
||||||
reencryptBotPrivateKeys,
|
|
||||||
reencryptSecretBlindIndexDataSalts,
|
|
||||||
} from "./reencryptData";
|
|
||||||
import {
|
import {
|
||||||
getClientIdGoogle,
|
getClientIdGoogle,
|
||||||
getClientSecretGoogle,
|
getClientSecretGoogle,
|
||||||
getMongoURL,
|
getMongoURL,
|
||||||
getNodeEnv,
|
getNodeEnv,
|
||||||
getSentryDSN,
|
getSentryDSN
|
||||||
} from "../../config";
|
} from "../../config";
|
||||||
import { initializePassport } from "../auth";
|
import { initializePassport } from "../auth";
|
||||||
|
|
||||||
@@ -79,6 +77,7 @@ export const setup = async () => {
|
|||||||
await backfillSecretFolders();
|
await backfillSecretFolders();
|
||||||
await backfillServiceToken();
|
await backfillServiceToken();
|
||||||
await backfillIntegration();
|
await backfillIntegration();
|
||||||
|
await backfillServiceTokenMultiScope();
|
||||||
|
|
||||||
// re-encrypt any data previously encrypted under server hex 128-bit ENCRYPTION_KEY
|
// re-encrypt any data previously encrypted under server hex 128-bit ENCRYPTION_KEY
|
||||||
// to base64 256-bit ROOT_ENCRYPTION_KEY
|
// to base64 256-bit ROOT_ENCRYPTION_KEY
|
||||||
@@ -90,7 +89,7 @@ export const setup = async () => {
|
|||||||
dsn: await getSentryDSN(),
|
dsn: await getSentryDSN(),
|
||||||
tracesSampleRate: 1.0,
|
tracesSampleRate: 1.0,
|
||||||
debug: (await getNodeEnv()) === "production" ? false : true,
|
debug: (await getNodeEnv()) === "production" ? false : true,
|
||||||
environment: await getNodeEnv(),
|
environment: await getNodeEnv()
|
||||||
});
|
});
|
||||||
|
|
||||||
await createTestUserForDevelopment();
|
await createTestUserForDevelopment();
|
||||||
|
|||||||
@@ -1,22 +1,19 @@
|
|||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import {
|
import {
|
||||||
ISecret,
|
ISecret,
|
||||||
IServiceAccount,
|
IServiceAccount,
|
||||||
IServiceTokenData,
|
IServiceTokenData,
|
||||||
IUser,
|
IUser,
|
||||||
ServiceAccount,
|
ServiceAccount,
|
||||||
ServiceTokenData,
|
ServiceTokenData,
|
||||||
User,
|
User
|
||||||
} from "../models";
|
} from "../models";
|
||||||
import {
|
import { ServiceTokenDataNotFoundError, UnauthorizedRequestError } from "../utils/errors";
|
||||||
ServiceTokenDataNotFoundError,
|
|
||||||
UnauthorizedRequestError,
|
|
||||||
} from "../utils/errors";
|
|
||||||
import {
|
import {
|
||||||
AUTH_MODE_API_KEY,
|
AUTH_MODE_API_KEY,
|
||||||
AUTH_MODE_JWT,
|
AUTH_MODE_JWT,
|
||||||
AUTH_MODE_SERVICE_ACCOUNT,
|
AUTH_MODE_SERVICE_ACCOUNT,
|
||||||
AUTH_MODE_SERVICE_TOKEN,
|
AUTH_MODE_SERVICE_TOKEN
|
||||||
} from "../variables";
|
} from "../variables";
|
||||||
import { validateUserClientForWorkspace } from "./user";
|
import { validateUserClientForWorkspace } from "./user";
|
||||||
import { validateServiceAccountClientForWorkspace } from "./serviceAccount";
|
import { validateServiceAccountClientForWorkspace } from "./serviceAccount";
|
||||||
@@ -30,65 +27,71 @@ import { validateServiceAccountClientForWorkspace } from "./serviceAccount";
|
|||||||
* @param {Array<'admin' | 'member'>} obj.acceptedRoles - accepted workspace roles
|
* @param {Array<'admin' | 'member'>} obj.acceptedRoles - accepted workspace roles
|
||||||
*/
|
*/
|
||||||
export const validateClientForServiceTokenData = async ({
|
export const validateClientForServiceTokenData = async ({
|
||||||
authData,
|
authData,
|
||||||
serviceTokenDataId,
|
serviceTokenDataId,
|
||||||
acceptedRoles,
|
acceptedRoles
|
||||||
}: {
|
}: {
|
||||||
authData: {
|
authData: {
|
||||||
authMode: string;
|
authMode: string;
|
||||||
authPayload: IUser | IServiceAccount | IServiceTokenData;
|
authPayload: IUser | IServiceAccount | IServiceTokenData;
|
||||||
};
|
};
|
||||||
serviceTokenDataId: Types.ObjectId;
|
serviceTokenDataId: Types.ObjectId;
|
||||||
acceptedRoles: Array<"admin" | "member">;
|
acceptedRoles: Array<"admin" | "member">;
|
||||||
}) => {
|
}) => {
|
||||||
const serviceTokenData = await ServiceTokenData
|
const serviceTokenData = await ServiceTokenData.findById(serviceTokenDataId)
|
||||||
.findById(serviceTokenDataId)
|
.select("+encryptedKey +iv +tag")
|
||||||
.select("+encryptedKey +iv +tag")
|
.populate<{ user: IUser }>("user");
|
||||||
.populate<{ user: IUser }>("user");
|
|
||||||
|
|
||||||
if (!serviceTokenData) throw ServiceTokenDataNotFoundError({
|
if (!serviceTokenData)
|
||||||
message: "Failed to find service token data",
|
throw ServiceTokenDataNotFoundError({
|
||||||
|
message: "Failed to find service token data"
|
||||||
});
|
});
|
||||||
|
|
||||||
if (authData.authMode === AUTH_MODE_JWT && authData.authPayload instanceof User) {
|
if (authData.authMode === AUTH_MODE_JWT && authData.authPayload instanceof User) {
|
||||||
await validateUserClientForWorkspace({
|
await validateUserClientForWorkspace({
|
||||||
user: authData.authPayload,
|
user: authData.authPayload,
|
||||||
workspaceId: serviceTokenData.workspace,
|
workspaceId: serviceTokenData.workspace,
|
||||||
acceptedRoles,
|
acceptedRoles
|
||||||
});
|
});
|
||||||
|
|
||||||
return serviceTokenData;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (authData.authMode === AUTH_MODE_SERVICE_ACCOUNT && authData.authPayload instanceof ServiceAccount) {
|
return serviceTokenData;
|
||||||
await validateServiceAccountClientForWorkspace({
|
}
|
||||||
serviceAccount: authData.authPayload,
|
|
||||||
workspaceId: serviceTokenData.workspace,
|
|
||||||
});
|
|
||||||
|
|
||||||
return serviceTokenData;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (authData.authMode === AUTH_MODE_SERVICE_TOKEN && authData.authPayload instanceof ServiceTokenData) {
|
if (
|
||||||
throw UnauthorizedRequestError({
|
authData.authMode === AUTH_MODE_SERVICE_ACCOUNT &&
|
||||||
message: "Failed service token authorization for service token data",
|
authData.authPayload instanceof ServiceAccount
|
||||||
});
|
) {
|
||||||
}
|
await validateServiceAccountClientForWorkspace({
|
||||||
|
serviceAccount: authData.authPayload,
|
||||||
|
workspaceId: serviceTokenData.workspace
|
||||||
|
});
|
||||||
|
|
||||||
if (authData.authMode === AUTH_MODE_API_KEY && authData.authPayload instanceof User) {
|
return serviceTokenData;
|
||||||
await validateUserClientForWorkspace({
|
}
|
||||||
user: authData.authPayload,
|
|
||||||
workspaceId: serviceTokenData.workspace,
|
if (
|
||||||
acceptedRoles,
|
authData.authMode === AUTH_MODE_SERVICE_TOKEN &&
|
||||||
});
|
authData.authPayload instanceof ServiceTokenData
|
||||||
|
) {
|
||||||
return serviceTokenData;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw UnauthorizedRequestError({
|
throw UnauthorizedRequestError({
|
||||||
message: "Failed client authorization for service token data",
|
message: "Failed service token authorization for service token data"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (authData.authMode === AUTH_MODE_API_KEY && authData.authPayload instanceof User) {
|
||||||
|
await validateUserClientForWorkspace({
|
||||||
|
user: authData.authPayload,
|
||||||
|
workspaceId: serviceTokenData.workspace,
|
||||||
|
acceptedRoles
|
||||||
|
});
|
||||||
|
|
||||||
|
return serviceTokenData;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw UnauthorizedRequestError({
|
||||||
|
message: "Failed client authorization for service token data"
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate that service token (client) can access workspace
|
* Validate that service token (client) can access workspace
|
||||||
@@ -101,42 +104,42 @@ export const validateClientForServiceTokenData = async ({
|
|||||||
* @param {String[]} requiredPermissions - required permissions as part of the endpoint
|
* @param {String[]} requiredPermissions - required permissions as part of the endpoint
|
||||||
*/
|
*/
|
||||||
export const validateServiceTokenDataClientForWorkspace = async ({
|
export const validateServiceTokenDataClientForWorkspace = async ({
|
||||||
serviceTokenData,
|
serviceTokenData,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
environment,
|
environment,
|
||||||
requiredPermissions,
|
requiredPermissions
|
||||||
}: {
|
}: {
|
||||||
serviceTokenData: IServiceTokenData;
|
serviceTokenData: IServiceTokenData;
|
||||||
workspaceId: Types.ObjectId;
|
workspaceId: Types.ObjectId;
|
||||||
environment?: string;
|
environment?: string;
|
||||||
requiredPermissions?: string[];
|
requiredPermissions?: string[];
|
||||||
}) => {
|
}) => {
|
||||||
if (!serviceTokenData.workspace.equals(workspaceId)) {
|
if (!serviceTokenData.workspace.equals(workspaceId)) {
|
||||||
// case: invalid workspaceId passed
|
// case: invalid workspaceId passed
|
||||||
|
throw UnauthorizedRequestError({
|
||||||
|
message: "Failed service token authorization for the given workspace"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (environment) {
|
||||||
|
// case: environment is specified
|
||||||
|
|
||||||
|
if (!serviceTokenData.scopes.find(({ environment: tkEnv }) => tkEnv === environment)) {
|
||||||
|
// case: invalid environment passed
|
||||||
|
throw UnauthorizedRequestError({
|
||||||
|
message: "Failed service token authorization for the given workspace environment"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
requiredPermissions?.forEach((permission) => {
|
||||||
|
if (!serviceTokenData.permissions.includes(permission)) {
|
||||||
throw UnauthorizedRequestError({
|
throw UnauthorizedRequestError({
|
||||||
message: "Failed service token authorization for the given workspace",
|
message: `Failed service token authorization for the given workspace environment action: ${permission}`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
if (environment) {
|
}
|
||||||
// case: environment is specified
|
};
|
||||||
|
|
||||||
if (serviceTokenData.environment !== environment) {
|
|
||||||
// case: invalid environment passed
|
|
||||||
throw UnauthorizedRequestError({
|
|
||||||
message: "Failed service token authorization for the given workspace environment",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
requiredPermissions?.forEach((permission) => {
|
|
||||||
if (!serviceTokenData.permissions.includes(permission)) {
|
|
||||||
throw UnauthorizedRequestError({
|
|
||||||
message: `Failed service token authorization for the given workspace environment action: ${permission}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate that service token (client) can access secrets
|
* Validate that service token (client) can access secrets
|
||||||
@@ -147,36 +150,35 @@ export const validateServiceTokenDataClientForWorkspace = async ({
|
|||||||
* @param {string[]} requiredPermissions - required permissions as part of the endpoint
|
* @param {string[]} requiredPermissions - required permissions as part of the endpoint
|
||||||
*/
|
*/
|
||||||
export const validateServiceTokenDataClientForSecrets = async ({
|
export const validateServiceTokenDataClientForSecrets = async ({
|
||||||
serviceTokenData,
|
serviceTokenData,
|
||||||
secrets,
|
secrets,
|
||||||
requiredPermissions,
|
requiredPermissions
|
||||||
}: {
|
}: {
|
||||||
serviceTokenData: IServiceTokenData;
|
serviceTokenData: IServiceTokenData;
|
||||||
secrets: ISecret[];
|
secrets: ISecret[];
|
||||||
requiredPermissions?: string[];
|
requiredPermissions?: string[];
|
||||||
}) => {
|
}) => {
|
||||||
|
secrets.forEach((secret: ISecret) => {
|
||||||
|
if (!serviceTokenData.workspace.equals(secret.workspace)) {
|
||||||
|
// case: invalid workspaceId passed
|
||||||
|
throw UnauthorizedRequestError({
|
||||||
|
message: "Failed service token authorization for the given workspace"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
secrets.forEach((secret: ISecret) => {
|
if (!serviceTokenData.scopes.find(({ environment: tkEnv }) => tkEnv === secret.environment)) {
|
||||||
if (!serviceTokenData.workspace.equals(secret.workspace)) {
|
// case: invalid environment passed
|
||||||
// case: invalid workspaceId passed
|
throw UnauthorizedRequestError({
|
||||||
throw UnauthorizedRequestError({
|
message: "Failed service token authorization for the given workspace environment"
|
||||||
message: "Failed service token authorization for the given workspace",
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
requiredPermissions?.forEach((permission) => {
|
||||||
if (serviceTokenData.environment !== secret.environment) {
|
if (!serviceTokenData.permissions.includes(permission)) {
|
||||||
// case: invalid environment passed
|
throw UnauthorizedRequestError({
|
||||||
throw UnauthorizedRequestError({
|
message: `Failed service token authorization for the given workspace environment action: ${permission}`
|
||||||
message: "Failed service token authorization for the given workspace environment",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
requiredPermissions?.forEach((permission) => {
|
|
||||||
if (!serviceTokenData.permissions.includes(permission)) {
|
|
||||||
throw UnauthorizedRequestError({
|
|
||||||
message: `Failed service token authorization for the given workspace environment action: ${permission}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user