diff --git a/README.md b/README.md
index 756f65f92..3a3860d2c 100644
--- a/README.md
+++ b/README.md
@@ -42,9 +42,9 @@
- **Navigate Multiple Environments** per project (e.g. development, staging, production, etc.)
- **Personal overrides** for secrets and configs
- **[Integrations](https://infisical.com/docs/integrations/overview)** with CI/CD and production infrastructure
-- **[Secret Versioning](https://infisical.com/docs/getting-started/dashboard/versioning)** - check the history of change for any secret
-- **[Activity Logs](https://infisical.com/docs/getting-started/dashboard/audit-logs)** - check what user in the project is performing what actions with secrets
-- **[Point-in-time Secrets Recovery](https://infisical.com/docs/getting-started/dashboard/pit-recovery)** - roll back to any snapshot of you secrets
+- **[Secret Versioning](https://infisical.com/docs/getting-started/dashboard/versioning)** to view the change history for any secret
+- **[Activity Logs](https://infisical.com/docs/getting-started/dashboard/audit-logs)** to record every action taken in a project.
+- **[Point-in-time Secrets Recovery](https://infisical.com/docs/getting-started/dashboard/pit-recovery)** for rolling back to any snapshot of your secrets
- 🔜 **1-Click Deploy** to Digital Ocean and Heroku
- 🔜 **Authentication/Authorization** for projects (read/write controls soon)
- 🔜 **Automatic Secret Rotation**
@@ -338,6 +338,10 @@ Infisical officially launched as v.1.0 on November 21st, 2022. There are a lot o
## 🌎 Translations
+<<<<<<< HEAD
+Infisical is currently aviable in English and Korean. Help us translate Infisical to your language!
+=======
Infisical is currently available in English and Korean. Help us translate Infisical to your language!
+>>>>>>> 9ce4a52b8da0057c2450cd7af93a8c5758c2476b
-You can find all the info in [this issue](https://github.com/Infisical/infisical/issues/181).
\ No newline at end of file
+You can find all the info in [this issue](https://github.com/Infisical/infisical/issues/181).
diff --git a/backend/package-lock.json b/backend/package-lock.json
index b51aa7cf0..38c1126cc 100644
--- a/backend/package-lock.json
+++ b/backend/package-lock.json
@@ -28,6 +28,7 @@
"express-validator": "^6.14.2",
"handlebars": "^4.7.7",
"helmet": "^5.1.1",
+ "js-yaml": "^4.1.0",
"jsonwebtoken": "^9.0.0",
"jsrp": "^0.2.4",
"libsodium-wrappers": "^0.7.10",
@@ -3698,8 +3699,7 @@
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
- "dev": true
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
},
"node_modules/array-flatten": {
"version": "1.1.1",
@@ -6638,7 +6638,6 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
- "dev": true,
"dependencies": {
"argparse": "^2.0.1"
},
@@ -14980,8 +14979,7 @@
"argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
- "dev": true
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
},
"array-flatten": {
"version": "1.1.1",
@@ -17197,7 +17195,6 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
- "dev": true,
"requires": {
"argparse": "^2.0.1"
}
diff --git a/backend/package.json b/backend/package.json
index 08f5a815c..b9359f680 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -5,7 +5,7 @@
"scripts": {
"start": "npm run build && node build/index.js",
"dev": "nodemon",
- "swagger-autogen": "node ./swagger.ts",
+ "swagger-autogen": "node ./swagger/index.ts",
"build": "rimraf ./build && tsc && cp -R ./src/templates ./build",
"lint": "eslint . --ext .ts",
"lint-and-fix": "eslint . --ext .ts --fix",
@@ -94,6 +94,7 @@
"express-validator": "^6.14.2",
"handlebars": "^4.7.7",
"helmet": "^5.1.1",
+ "js-yaml": "^4.1.0",
"jsonwebtoken": "^9.0.0",
"jsrp": "^0.2.4",
"libsodium-wrappers": "^0.7.10",
diff --git a/backend/api-documentation.json b/backend/spec.json
similarity index 65%
rename from backend/api-documentation.json
rename to backend/spec.json
index 79cf0eb3b..e28c66eaa 100644
--- a/backend/api-documentation.json
+++ b/backend/spec.json
@@ -5,10 +5,21 @@
"description": "List of all available APIs that can be consumed",
"version": "1.0.0"
},
+ "servers": [
+ {
+ "url": "https://infisical.com",
+ "description": "Production server"
+ },
+ {
+ "url": "http://localhost:8080",
+ "description": "Local server"
+ }
+ ],
"paths": {
"/api/v1/secret/{secretId}/secret-versions": {
"get": {
- "description": "",
+ "summary": "Return secret versions",
+ "description": "Return secret versions",
"parameters": [
{
"name": "secretId",
@@ -16,10 +27,13 @@
"required": true,
"schema": {
"type": "string"
- }
+ },
+ "description": "ID of secret"
},
{
"name": "offset",
+ "description": "Number of versions to skip",
+ "required": false,
"in": "query",
"schema": {
"type": "string"
@@ -27,6 +41,8 @@
},
{
"name": "limit",
+ "description": "Maximum number of versions to return",
+ "required": false,
"in": "query",
"schema": {
"type": "string"
@@ -35,11 +51,92 @@
],
"responses": {
"200": {
- "description": "OK"
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "secretVersions": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/SecretVersion"
+ },
+ "description": "Secret versions"
+ }
+ }
+ }
+ }
+ }
},
"400": {
"description": "Bad Request"
}
+ },
+ "security": [
+ {
+ "apiKeyAuth": []
+ }
+ ]
+ }
+ },
+ "/api/v1/secret/{secretId}/secret-versions/rollback": {
+ "post": {
+ "summary": "Roll back secret to a version.",
+ "description": "Roll back secret to a version.",
+ "parameters": [
+ {
+ "name": "secretId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ },
+ "description": "ID of secret"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "secret": {
+ "type": "object",
+ "$ref": "#/components/schemas/Secret",
+ "description": "Secret rolled back to"
+ }
+ }
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request"
+ }
+ },
+ "security": [
+ {
+ "apiKeyAuth": []
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "version": {
+ "type": "integer",
+ "description": "Version of secret to roll back to"
+ }
+ }
+ }
+ }
+ }
}
}
},
@@ -68,7 +165,8 @@
},
"/api/v1/workspace/{workspaceId}/secret-snapshots": {
"get": {
- "description": "",
+ "summary": "Return project secret snapshot ids",
+ "description": "Return project secret snapshots ids",
"parameters": [
{
"name": "workspaceId",
@@ -76,10 +174,13 @@
"required": true,
"schema": {
"type": "string"
- }
+ },
+ "description": "ID of project"
},
{
"name": "offset",
+ "description": "Number of secret snapshots to skip",
+ "required": false,
"in": "query",
"schema": {
"type": "string"
@@ -87,6 +188,8 @@
},
{
"name": "limit",
+ "description": "Maximum number of secret snapshots to return",
+ "required": false,
"in": "query",
"schema": {
"type": "string"
@@ -95,12 +198,33 @@
],
"responses": {
"200": {
- "description": "OK"
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "secretSnapshots": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/SecretSnapshot"
+ },
+ "description": "Project secret snapshots"
+ }
+ }
+ }
+ }
+ }
},
"400": {
"description": "Bad Request"
}
- }
+ },
+ "security": [
+ {
+ "apiKeyAuth": []
+ }
+ ]
}
},
"/api/v1/workspace/{workspaceId}/secret-snapshots/count": {
@@ -126,20 +250,95 @@
}
}
},
- "/api/v1/workspace/{workspaceId}/logs": {
- "get": {
- "description": "",
+ "/api/v1/workspace/{workspaceId}/secret-snapshots/rollback": {
+ "post": {
+ "summary": "Roll back project secrets to those captured in a secret snapshot version.",
+ "description": "Roll back project secrets to those captured in a secret snapshot version.",
"parameters": [
{
"name": "workspaceId",
"in": "path",
"required": true,
+ "schema": {
+ "type": "string"
+ },
+ "description": "ID of project"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "secrets": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Secret"
+ },
+ "description": "Secrets rolled back to"
+ }
+ }
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request"
+ }
+ },
+ "security": [
+ {
+ "apiKeyAuth": []
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "version": {
+ "type": "integer",
+ "description": "Version of secret snapshot to roll back to"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/workspace/{workspaceId}/logs": {
+ "get": {
+ "summary": "Return project (audit) logs",
+ "description": "Return project (audit) logs",
+ "parameters": [
+ {
+ "name": "workspaceId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ },
+ "description": "ID of project"
+ },
+ {
+ "name": "userId",
+ "description": "ID of project member",
+ "required": false,
+ "in": "query",
"schema": {
"type": "string"
}
},
{
"name": "offset",
+ "description": "Number of logs to skip",
+ "required": false,
"in": "query",
"schema": {
"type": "string"
@@ -147,6 +346,8 @@
},
{
"name": "limit",
+ "description": "Maximum number of logs to return",
+ "required": false,
"in": "query",
"schema": {
"type": "string"
@@ -154,20 +355,21 @@
},
{
"name": "sortBy",
- "in": "query",
+ "description": "Order to sort the logs by",
"schema": {
- "type": "string"
- }
- },
- {
- "name": "userId",
- "in": "query",
- "schema": {
- "type": "string"
- }
+ "type": "string",
+ "enum": [
+ "oldest",
+ "recent"
+ ]
+ },
+ "required": false,
+ "in": "query"
},
{
"name": "actionNames",
+ "description": "Names of log actions (comma-separated)",
+ "required": false,
"in": "query",
"schema": {
"type": "string"
@@ -176,12 +378,33 @@
],
"responses": {
"200": {
- "description": "OK"
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "logs": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Log"
+ },
+ "description": "Project logs"
+ }
+ }
+ }
+ }
+ }
},
"400": {
"description": "Bad Request"
}
- }
+ },
+ "security": [
+ {
+ "apiKeyAuth": []
+ }
+ ]
}
},
"/api/v1/action/{actionId}": {
@@ -476,7 +699,11 @@
"post": {
"description": "",
"parameters": [],
- "responses": {}
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
}
},
"/api/v1/bot/{workspaceId}": {
@@ -1997,6 +2224,40 @@
}
}
},
+ "/api/v2/users/me": {
+ "get": {
+ "summary": "Retrieve the current user on the request",
+ "description": "Retrieve the current user on the request",
+ "parameters": [],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "user": {
+ "type": "object",
+ "$ref": "#/components/schemas/CurrentUser",
+ "description": "Current user on request"
+ }
+ }
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request"
+ }
+ },
+ "security": [
+ {
+ "apiKeyAuth": []
+ }
+ ]
+ }
+ },
"/api/v2/workspace/{workspaceId}/secrets": {
"post": {
"description": "",
@@ -2080,7 +2341,8 @@
},
"/api/v2/workspace/{workspaceId}/encrypted-key": {
"get": {
- "description": "",
+ "summary": "Return encrypted project key",
+ "description": "Return encrypted project key",
"parameters": [
{
"name": "workspaceId",
@@ -2088,17 +2350,34 @@
"required": true,
"schema": {
"type": "string"
- }
+ },
+ "description": "ID of project"
}
],
"responses": {
"200": {
- "description": "OK"
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ProjectKey"
+ },
+ "description": "Encrypted project key for the given project"
+ }
+ }
+ }
},
"400": {
"description": "Bad Request"
}
- }
+ },
+ "security": [
+ {
+ "apiKeyAuth": []
+ }
+ ]
}
},
"/api/v2/workspace/{workspaceId}/service-token-data": {
@@ -2124,7 +2403,171 @@
}
}
},
- "/api/v2/secret/batch-create/workspace/{workspaceId}/environment/{environmentName}": {
+ "/api/v2/workspace/{workspaceId}/memberships": {
+ "get": {
+ "summary": "Return project memberships",
+ "description": "Return project memberships",
+ "parameters": [
+ {
+ "name": "workspaceId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ },
+ "description": "ID of project"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "memberships": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Membership"
+ },
+ "description": "Memberships of project"
+ }
+ }
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request"
+ }
+ },
+ "security": [
+ {
+ "apiKeyAuth": []
+ }
+ ]
+ }
+ },
+ "/api/v2/workspace/{workspaceId}/memberships/{membershipId}": {
+ "delete": {
+ "summary": "Delete project membership",
+ "description": "Delete project membership",
+ "parameters": [
+ {
+ "name": "workspaceId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ },
+ "description": "ID of project"
+ },
+ {
+ "name": "membershipId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ },
+ "description": "ID of membership"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "membership": {
+ "$ref": "#/components/schemas/Membership",
+ "description": "Deleted membership"
+ }
+ }
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request"
+ }
+ },
+ "security": [
+ {
+ "apiKeyAuth": []
+ }
+ ]
+ },
+ "patch": {
+ "summary": "Update project membership",
+ "description": "Update project membership",
+ "parameters": [
+ {
+ "name": "workspaceId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ },
+ "description": "ID of project"
+ },
+ {
+ "name": "membershipId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ },
+ "description": "ID of membership"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "membership": {
+ "$ref": "#/components/schemas/Membership",
+ "description": "Updated membership"
+ }
+ }
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request"
+ }
+ },
+ "security": [
+ {
+ "apiKeyAuth": []
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "role": {
+ "type": "string",
+ "description": "Role of membership - either admin or member"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/secret/batch-create/workspace/{workspaceId}/environment/{environment}": {
"post": {
"description": "",
"parameters": [
@@ -2137,7 +2580,7 @@
}
},
{
- "name": "environmentName",
+ "name": "environment",
"in": "path",
"required": true,
"schema": {
@@ -2166,7 +2609,7 @@
}
}
},
- "/api/v2/secret/workspace/{workspaceId}/environment/{environmentName}": {
+ "/api/v2/secret/workspace/{workspaceId}/environment/{environment}": {
"post": {
"description": "",
"parameters": [
@@ -2179,47 +2622,7 @@
}
},
{
- "name": "environmentName",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK"
- }
- },
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "secret": {
- "example": "any"
- }
- }
- }
- }
- }
- }
- },
- "patch": {
- "description": "",
- "parameters": [
- {
- "name": "workspaceId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "environmentName",
+ "name": "environment",
"in": "path",
"required": true,
"schema": {
@@ -2397,6 +2800,256 @@
}
}
},
+ "/api/v2/secret/workspace/{workspaceId}/environment/{environmentName}": {
+ "patch": {
+ "description": "",
+ "parameters": [
+ {
+ "name": "workspaceId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "environmentName",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ },
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "secret": {
+ "example": "any"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/secrets/": {
+ "post": {
+ "summary": "Create new secret(s)",
+ "description": "Create one or many secrets for a given project and environment.",
+ "parameters": [],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "secrets": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Secret"
+ },
+ "description": "Newly-created secrets for the given project and environment"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "apiKeyAuth": []
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "workspaceId": {
+ "type": "string",
+ "description": "ID of project"
+ },
+ "environment": {
+ "type": "string",
+ "description": "Environment within project"
+ },
+ "secrets": {
+ "$ref": "#/components/schemas/CreateSecret",
+ "description": "Secret(s) to create - object or array of objects"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "get": {
+ "summary": "Read secrets",
+ "description": "Read secrets from a project and environment",
+ "parameters": [
+ {
+ "name": "workspaceId",
+ "description": "ID of project",
+ "required": true,
+ "in": "query",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "environment",
+ "description": "Environment within project",
+ "required": true,
+ "in": "query",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "content",
+ "in": "query",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "secrets": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Secret"
+ },
+ "description": "Secrets for the given project and environment"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "apiKeyAuth": []
+ }
+ ]
+ },
+ "patch": {
+ "summary": "Update secret(s)",
+ "description": "Update secret(s)",
+ "parameters": [],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "secrets": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Secret"
+ },
+ "description": "Updated secrets"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "apiKeyAuth": []
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "secrets": {
+ "$ref": "#/components/schemas/UpdateSecret",
+ "description": "Secret(s) to update - object or array of objects"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "delete": {
+ "summary": "Delete secret(s)",
+ "description": "Delete one or many secrets by their ID(s)",
+ "parameters": [],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "secrets": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Secret"
+ },
+ "description": "Deleted secrets"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "apiKeyAuth": []
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "secretIds": {
+ "type": "string",
+ "description": "ID(s) of secrets - string or array of strings"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"/api/v2/service-token/": {
"get": {
"description": "",
@@ -2557,11 +3210,478 @@
}
},
"components": {
+ "schemas": {
+ "CurrentUser": {
+ "type": "object",
+ "properties": {
+ "_id": {
+ "type": "string",
+ "example": ""
+ },
+ "email": {
+ "type": "string",
+ "example": ""
+ },
+ "firstName": {
+ "type": "string",
+ "example": ""
+ },
+ "lastName": {
+ "type": "string",
+ "example": ""
+ },
+ "publicKey": {
+ "type": "string",
+ "example": ""
+ },
+ "encryptedPrivateKey": {
+ "type": "string",
+ "example": ""
+ },
+ "iv": {
+ "type": "string",
+ "example": ""
+ },
+ "tag": {
+ "type": "string",
+ "example": ""
+ },
+ "updatedAt": {
+ "type": "string",
+ "example": ""
+ },
+ "createdAt": {
+ "type": "string",
+ "example": ""
+ }
+ }
+ },
+ "Membership": {
+ "type": "object",
+ "properties": {
+ "user": {
+ "type": "object",
+ "properties": {
+ "_id": {
+ "type": "string",
+ "example": ""
+ },
+ "email": {
+ "type": "string",
+ "example": ""
+ },
+ "firstName": {
+ "type": "string",
+ "example": ""
+ },
+ "lastName": {
+ "type": "string",
+ "example": ""
+ },
+ "publicKey": {
+ "type": "string",
+ "example": ""
+ },
+ "updatedAt": {
+ "type": "string",
+ "example": ""
+ },
+ "createdAt": {
+ "type": "string",
+ "example": ""
+ }
+ }
+ },
+ "workspace": {
+ "type": "string",
+ "example": ""
+ },
+ "role": {
+ "type": "string",
+ "example": "admin"
+ }
+ }
+ },
+ "ProjectKey": {
+ "type": "object",
+ "properties": {
+ "encryptedkey": {
+ "type": "string",
+ "example": ""
+ },
+ "nonce": {
+ "type": "string",
+ "example": ""
+ },
+ "sender": {
+ "type": "object",
+ "properties": {
+ "publicKey": {
+ "type": "string",
+ "example": ""
+ }
+ }
+ },
+ "receiver": {
+ "type": "string",
+ "example": ""
+ },
+ "workspace": {
+ "type": "string",
+ "example": ""
+ }
+ }
+ },
+ "CreateSecret": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "example": "shared"
+ },
+ "secretKeyCiphertext": {
+ "type": "string",
+ "example": ""
+ },
+ "secretKeyIV": {
+ "type": "string",
+ "example": ""
+ },
+ "secretKeyTag": {
+ "type": "string",
+ "example": ""
+ },
+ "secretValueCiphertext": {
+ "type": "string",
+ "example": ""
+ },
+ "secretValueIV": {
+ "type": "string",
+ "example": ""
+ },
+ "secretValueTag": {
+ "type": "string",
+ "example": ""
+ },
+ "secretCommentCiphertext": {
+ "type": "string",
+ "example": ""
+ },
+ "secretCommentIV": {
+ "type": "string",
+ "example": ""
+ },
+ "secretCommentTag": {
+ "type": "string",
+ "example": ""
+ }
+ }
+ },
+ "UpdateSecret": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "example": ""
+ },
+ "secretKeyCiphertext": {
+ "type": "string",
+ "example": ""
+ },
+ "secretKeyIV": {
+ "type": "string",
+ "example": ""
+ },
+ "secretKeyTag": {
+ "type": "string",
+ "example": ""
+ },
+ "secretValueCiphertext": {
+ "type": "string",
+ "example": ""
+ },
+ "secretValueIV": {
+ "type": "string",
+ "example": ""
+ },
+ "secretValueTag": {
+ "type": "string",
+ "example": ""
+ },
+ "secretCommentCiphertext": {
+ "type": "string",
+ "example": ""
+ },
+ "secretCommentIV": {
+ "type": "string",
+ "example": ""
+ },
+ "secretCommentTag": {
+ "type": "string",
+ "example": ""
+ }
+ }
+ },
+ "Secret": {
+ "type": "object",
+ "properties": {
+ "_id": {
+ "type": "string",
+ "example": ""
+ },
+ "version": {
+ "type": "number",
+ "example": 1
+ },
+ "workspace": {
+ "type": "string",
+ "example": ""
+ },
+ "type": {
+ "type": "string",
+ "example": "shared"
+ },
+ "user": {},
+ "secretKeyCiphertext": {
+ "type": "string",
+ "example": ""
+ },
+ "secretKeyIV": {
+ "type": "string",
+ "example": ""
+ },
+ "secretKeyTag": {
+ "type": "string",
+ "example": ""
+ },
+ "secretValueCiphertext": {
+ "type": "string",
+ "example": ""
+ },
+ "secretValueIV": {
+ "type": "string",
+ "example": ""
+ },
+ "secretValueTag": {
+ "type": "string",
+ "example": ""
+ },
+ "secretCommentCiphertext": {
+ "type": "string",
+ "example": ""
+ },
+ "secretCommentIV": {
+ "type": "string",
+ "example": ""
+ },
+ "secretCommentTag": {
+ "type": "string",
+ "example": ""
+ },
+ "updatedAt": {
+ "type": "string",
+ "example": ""
+ },
+ "createdAt": {
+ "type": "string",
+ "example": ""
+ }
+ }
+ },
+ "Log": {
+ "type": "object",
+ "properties": {
+ "_id": {
+ "type": "string",
+ "example": ""
+ },
+ "user": {
+ "type": "object",
+ "properties": {
+ "_id": {
+ "type": "string",
+ "example": ""
+ },
+ "email": {
+ "type": "string",
+ "example": ""
+ },
+ "firstName": {
+ "type": "string",
+ "example": ""
+ },
+ "lastName": {
+ "type": "string",
+ "example": ""
+ }
+ }
+ },
+ "workspace": {
+ "type": "string",
+ "example": ""
+ },
+ "actionNames": {
+ "type": "array",
+ "example": [
+ "addSecrets"
+ ],
+ "items": {
+ "type": "string"
+ }
+ },
+ "actions": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "example": "addSecrets"
+ },
+ "user": {
+ "type": "string",
+ "example": ""
+ },
+ "workspace": {
+ "type": "string",
+ "example": ""
+ },
+ "payload": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "oldSecretVersion": {
+ "type": "string",
+ "example": ""
+ },
+ "newSecretVersion": {
+ "type": "string",
+ "example": ""
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "channel": {
+ "type": "string",
+ "example": "cli"
+ },
+ "ipAddress": {
+ "type": "string",
+ "example": "192.168.0.1"
+ },
+ "updatedAt": {
+ "type": "string",
+ "example": ""
+ },
+ "createdAt": {
+ "type": "string",
+ "example": ""
+ }
+ }
+ },
+ "SecretSnapshot": {
+ "type": "object",
+ "properties": {
+ "workspace": {
+ "type": "string",
+ "example": ""
+ },
+ "version": {
+ "type": "number",
+ "example": 1
+ },
+ "secretVersions": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "_id": {
+ "type": "string",
+ "example": ""
+ }
+ }
+ }
+ }
+ }
+ },
+ "SecretVersion": {
+ "type": "object",
+ "properties": {
+ "_id": {
+ "type": "string",
+ "example": ""
+ },
+ "secret": {
+ "type": "string",
+ "example": ""
+ },
+ "version": {
+ "type": "number",
+ "example": 1
+ },
+ "workspace": {
+ "type": "string",
+ "example": ""
+ },
+ "type": {
+ "type": "string",
+ "example": ""
+ },
+ "user": {
+ "type": "string",
+ "example": ""
+ },
+ "environment": {
+ "type": "string",
+ "example": ""
+ },
+ "isDeleted": {
+ "type": "string",
+ "example": ""
+ },
+ "secretKeyCiphertext": {
+ "type": "string",
+ "example": ""
+ },
+ "secretKeyIV": {
+ "type": "string",
+ "example": ""
+ },
+ "secretKeyTag": {
+ "type": "string",
+ "example": ""
+ },
+ "secretValueCiphertext": {
+ "type": "string",
+ "example": ""
+ },
+ "secretValueIV": {
+ "type": "string",
+ "example": ""
+ },
+ "secretValueTag": {
+ "type": "string",
+ "example": ""
+ }
+ }
+ }
+ },
"securitySchemes": {
"bearerAuth": {
"type": "http",
"scheme": "bearer",
- "bearerFormat": "JWT"
+ "bearerFormat": "JWT",
+ "description": "This security definition uses the HTTP 'bearer' scheme, which allows the client to authenticate using a JSON Web Token (JWT) that is passed in the Authorization header of the request."
+ },
+ "apiKeyAuth": {
+ "type": "apiKey",
+ "in": "header",
+ "name": "X-API-Key",
+ "description": "This security definition uses an API key, which is passed in the header of the request as the value of the \"X-API-Key\" header. The client must provide a valid key in order to access the API."
}
}
}
diff --git a/backend/src/app.ts b/backend/src/app.ts
index 82275b9c9..d32e83be7 100644
--- a/backend/src/app.ts
+++ b/backend/src/app.ts
@@ -8,7 +8,7 @@ import cookieParser from 'cookie-parser';
import dotenv from 'dotenv';
import swaggerUi = require('swagger-ui-express');
// eslint-disable-next-line @typescript-eslint/no-var-requires
-const swaggerFile = require('../api-documentation.json')
+const swaggerFile = require('../spec.json')
dotenv.config();
@@ -41,7 +41,8 @@ import {
integrationAuth as v1IntegrationAuthRouter
} from './routes/v1';
import {
- secret as v2SecretRouter,
+ users as v2UsersRouter,
+ secret as v2SecretRouter, // begin to phase out
secrets as v2SecretsRouter,
workspace as v2WorkspaceRouter,
serviceTokenData as v2ServiceTokenDataRouter,
@@ -104,6 +105,7 @@ app.use('/api/v1/integration', v1IntegrationRouter);
app.use('/api/v1/integration-auth', v1IntegrationAuthRouter);
// v2 routes
+app.use('/api/v2/users', v2UsersRouter);
app.use('/api/v2/workspace', v2EnvironmentRouter);
app.use('/api/v2/workspace', v2WorkspaceRouter); // TODO: turn into plural route
app.use('/api/v2/secret', v2SecretRouter); // stop supporting, TODO: revise
diff --git a/backend/src/controllers/v1/authController.ts b/backend/src/controllers/v1/authController.ts
index defd03d8a..882db688f 100644
--- a/backend/src/controllers/v1/authController.ts
+++ b/backend/src/controllers/v1/authController.ts
@@ -170,10 +170,11 @@ export const logout = async (req: Request, res: Response) => {
* @param res
* @returns
*/
-export const checkAuth = async (req: Request, res: Response) =>
- res.status(200).send({
+export const checkAuth = async (req: Request, res: Response) => {
+ return res.status(200).send({
message: 'Authenticated'
});
+}
/**
* Return new token by redeeming refresh token
diff --git a/backend/src/controllers/v2/index.ts b/backend/src/controllers/v2/index.ts
index 601314a2a..b2b245d9c 100644
--- a/backend/src/controllers/v2/index.ts
+++ b/backend/src/controllers/v2/index.ts
@@ -1,3 +1,4 @@
+import * as usersController from './usersController';
import * as workspaceController from './workspaceController';
import * as serviceTokenDataController from './serviceTokenDataController';
import * as apiKeyDataController from './apiKeyDataController';
@@ -6,6 +7,7 @@ import * as secretsController from './secretsController';
import * as environmentController from './environmentController';
export {
+ usersController,
workspaceController,
serviceTokenDataController,
apiKeyDataController,
diff --git a/backend/src/controllers/v2/secretsController.ts b/backend/src/controllers/v2/secretsController.ts
index 098cfed6d..77a318fc6 100644
--- a/backend/src/controllers/v2/secretsController.ts
+++ b/backend/src/controllers/v2/secretsController.ts
@@ -23,6 +23,58 @@ import { BadRequestError } from '../../utils/errors';
* @param res
*/
export const createSecrets = async (req: Request, res: Response) => {
+ /*
+ #swagger.summary = 'Create new secret(s)'
+ #swagger.description = 'Create one or many secrets for a given project and environment.'
+
+ #swagger.security = [{
+ "apiKeyAuth": []
+ }]
+
+ #swagger.requestBody = {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "workspaceId": {
+ "type": "string",
+ "description": "ID of project",
+ },
+ "environment": {
+ "type": "string",
+ "description": "Environment within project"
+ },
+ "secrets": {
+ $ref: "#/components/schemas/CreateSecret",
+ "description": "Secret(s) to create - object or array of objects"
+ }
+ }
+ }
+ }
+ }
+ }
+
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "secrets": {
+ "type": "array",
+ "items": {
+ $ref: "#/components/schemas/Secret"
+ },
+ "description": "Newly-created secrets for the given project and environment"
+ }
+ }
+ }
+ }
+ }
+ }
+ */
const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli';
const { workspaceId, environment } = req.body;
@@ -161,6 +213,45 @@ export const createSecrets = async (req: Request, res: Response) => {
* @returns
*/
export const getSecrets = async (req: Request, res: Response) => {
+ /*
+ #swagger.summary = 'Read secrets'
+ #swagger.description = 'Read secrets from a project and environment'
+
+ #swagger.security = [{
+ "apiKeyAuth": []
+ }]
+
+ #swagger.parameters['workspaceId'] = {
+ "description": "ID of project",
+ "required": true,
+ "type": "string"
+ }
+
+ #swagger.parameters['environment'] = {
+ "description": "Environment within project",
+ "required": true,
+ "type": "string"
+ }
+
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "secrets": {
+ "type": "array",
+ "items": {
+ $ref: "#/components/schemas/Secret"
+ },
+ "description": "Secrets for the given project and environment"
+ }
+ }
+ }
+ }
+ }
+ }
+ */
const { workspaceId, environment } = req.query;
let userId: Types.ObjectId | undefined = undefined // used for getting personal secrets for user
@@ -231,6 +322,50 @@ export const getSecrets = async (req: Request, res: Response) => {
* @param res
*/
export const updateSecrets = async (req: Request, res: Response) => {
+ /*
+ #swagger.summary = 'Update secret(s)'
+ #swagger.description = 'Update secret(s)'
+
+ #swagger.security = [{
+ "apiKeyAuth": []
+ }]
+
+ #swagger.requestBody = {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "secrets": {
+ $ref: "#/components/schemas/UpdateSecret",
+ "description": "Secret(s) to update - object or array of objects"
+ }
+ }
+ }
+ }
+ }
+ }
+
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "secrets": {
+ "type": "array",
+ "items": {
+ $ref: "#/components/schemas/Secret"
+ },
+ "description": "Updated secrets"
+ }
+ }
+ }
+ }
+ }
+ }
+ */
const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli';
// TODO: move type
@@ -403,6 +538,50 @@ export const updateSecrets = async (req: Request, res: Response) => {
* @param res
*/
export const deleteSecrets = async (req: Request, res: Response) => {
+ /*
+ #swagger.summary = 'Delete secret(s)'
+ #swagger.description = 'Delete one or many secrets by their ID(s)'
+
+ #swagger.security = [{
+ "apiKeyAuth": []
+ }]
+
+ #swagger.requestBody = {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "secretIds": {
+ "type": "string",
+ "description": "ID(s) of secrets - string or array of strings"
+ },
+ }
+ }
+ }
+ }
+ }
+
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "secrets": {
+ "type": "array",
+ "items": {
+ $ref: "#/components/schemas/Secret"
+ },
+ "description": "Deleted secrets"
+ }
+ }
+ }
+ }
+ }
+ }
+ */
const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli';
const toDelete = req.secrets.map((s: any) => s._id);
diff --git a/backend/src/controllers/v2/usersController.ts b/backend/src/controllers/v2/usersController.ts
new file mode 100644
index 000000000..7ad247ff0
--- /dev/null
+++ b/backend/src/controllers/v2/usersController.ts
@@ -0,0 +1,49 @@
+import { Request, Response } from 'express';
+import * as Sentry from '@sentry/node';
+import {
+ User
+} from '../../models';
+
+export const getMe = async (req: Request, res: Response) => {
+ /*
+ #swagger.summary = "Retrieve the current user on the request"
+ #swagger.description = "Retrieve the current user on the request"
+
+ #swagger.security = [{
+ "apiKeyAuth": []
+ }]
+
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "user": {
+ "type": "object",
+ $ref: "#/components/schemas/CurrentUser",
+ "description": "Current user on request"
+ }
+ }
+ }
+ }
+ }
+ }
+ */
+ let user;
+ try {
+ user = await User
+ .findById(req.user._id)
+ .select('+publicKey +encryptedPrivateKey +iv +tag');
+ } catch (err) {
+ Sentry.setUser({ email: req.user.email });
+ Sentry.captureException(err);
+ return res.status(400).send({
+ message: 'Failed to get user'
+ });
+ }
+
+ return res.status(200).send({
+ user
+ });
+}
\ No newline at end of file
diff --git a/backend/src/controllers/v2/workspaceController.ts b/backend/src/controllers/v2/workspaceController.ts
index efac159a5..39a1be5fd 100644
--- a/backend/src/controllers/v2/workspaceController.ts
+++ b/backend/src/controllers/v2/workspaceController.ts
@@ -1,7 +1,9 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
+import { Types } from 'mongoose';
import {
Workspace,
+ Secret,
Membership,
MembershipOrg,
Integration,
@@ -174,6 +176,34 @@ export const pullSecrets = async (req: Request, res: Response) => {
};
export const getWorkspaceKey = async (req: Request, res: Response) => {
+ /*
+ #swagger.summary = 'Return encrypted project key'
+ #swagger.description = 'Return encrypted project key'
+
+ #swagger.security = [{
+ "apiKeyAuth": []
+ }]
+
+ #swagger.parameters['workspaceId'] = {
+ "description": "ID of project",
+ "required": true,
+ "type": "string"
+ }
+
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ $ref: "#/components/schemas/ProjectKey"
+ },
+ "description": "Encrypted project key for the given project"
+ }
+ }
+ }
+ }
+ */
let key;
try {
const { workspaceId } = req.params;
@@ -219,4 +249,222 @@ export const getWorkspaceServiceTokenData = async (
return res.status(200).send({
serviceTokenData
});
+}
+
+/**
+ * Return memberships for workspace with id [workspaceId]
+ * @param req
+ * @param res
+ * @returns
+ */
+export const getWorkspaceMemberships = async (req: Request, res: Response) => {
+ /*
+ #swagger.summary = 'Return project memberships'
+ #swagger.description = 'Return project memberships'
+
+ #swagger.security = [{
+ "apiKeyAuth": []
+ }]
+
+ #swagger.parameters['workspaceId'] = {
+ "description": "ID of project",
+ "required": true,
+ "type": "string"
+ }
+
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "memberships": {
+ "type": "array",
+ "items": {
+ $ref: "#/components/schemas/Membership"
+ },
+ "description": "Memberships of project"
+ }
+ }
+ }
+ }
+ }
+ }
+ */
+ let memberships;
+ try {
+ const { workspaceId } = req.params;
+
+ memberships = await Membership.find({
+ workspace: workspaceId
+ }).populate('user', '+publicKey');
+ } catch (err) {
+ Sentry.setUser({ email: req.user.email });
+ Sentry.captureException(err);
+ return res.status(400).send({
+ message: 'Failed to get workspace memberships'
+ });
+ }
+
+ return res.status(200).send({
+ memberships
+ });
+}
+
+/**
+ * Delete workspace membership with id [membershipId]
+ * @param req
+ * @param res
+ * @returns
+ */
+export const deleteWorkspaceMembership = async (req: Request, res: Response) => {
+ /*
+ #swagger.summary = 'Delete project membership'
+ #swagger.description = 'Delete project membership'
+
+ #swagger.security = [{
+ "apiKeyAuth": []
+ }]
+
+ #swagger.parameters['workspaceId'] = {
+ "description": "ID of project",
+ "required": true,
+ "type": "string"
+ }
+
+ #swagger.parameters['membershipId'] = {
+ "description": "ID of membership",
+ "required": true,
+ "type": "string"
+ }
+
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "membership": {
+ $ref: "#/components/schemas/Membership",
+ "description": "Deleted membership"
+ }
+ }
+ }
+ }
+ }
+ }
+ */
+ let membership;
+ try {
+ const {
+ membershipId
+ } = req.params;
+
+ membership = await Membership.findByIdAndDelete(membershipId);
+
+ if (!membership) throw new Error('Failed to delete workspace membership');
+
+ await Key.deleteMany({
+ receiver: membership.user,
+ workspace: membership.workspace
+ });
+ } catch (err) {
+ Sentry.setUser({ email: req.user.email });
+ Sentry.captureException(err);
+ return res.status(400).send({
+ message: 'Failed to delete workspace membership'
+ });
+ }
+
+ return res.status(200).send({
+ membership
+ });
+}
+
+/**
+ * Update role of membership with id [membershipId] to role [role]
+ * @param req
+ * @param res
+ * @returns
+ */
+export const updateWorkspaceMembership = async (req: Request, res: Response) => {
+ /*
+ #swagger.summary = 'Update project membership'
+ #swagger.description = 'Update project membership'
+
+ #swagger.security = [{
+ "apiKeyAuth": []
+ }]
+
+ #swagger.parameters['workspaceId'] = {
+ "description": "ID of project",
+ "required": true,
+ "type": "string"
+ }
+
+ #swagger.parameters['membershipId'] = {
+ "description": "ID of membership",
+ "required": true,
+ "type": "string"
+ }
+
+ #swagger.requestBody = {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "role": {
+ "type": "string",
+ "description": "Role of membership - either admin or member",
+ }
+ }
+ }
+ }
+ }
+ }
+
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "membership": {
+ $ref: "#/components/schemas/Membership",
+ "description": "Updated membership"
+ }
+ }
+ }
+ }
+ }
+ }
+ */
+ let membership;
+ try {
+ const {
+ membershipId
+ } = req.params;
+ const { role } = req.body;
+
+ membership = await Membership.findByIdAndUpdate(
+ membershipId,
+ {
+ role
+ }, {
+ new: true
+ }
+ );
+ } catch (err) {
+ Sentry.setUser({ email: req.user.email });
+ Sentry.captureException(err);
+ return res.status(400).send({
+ message: 'Failed to update workspace membership'
+ });
+ }
+
+ return res.status(200).send({
+ membership
+ });
}
\ No newline at end of file
diff --git a/backend/src/ee/controllers/v1/secretController.ts b/backend/src/ee/controllers/v1/secretController.ts
index 751f21611..562c8aa88 100644
--- a/backend/src/ee/controllers/v1/secretController.ts
+++ b/backend/src/ee/controllers/v1/secretController.ts
@@ -10,6 +10,51 @@ import { EESecretService } from '../../services';
* @param res
*/
export const getSecretVersions = async (req: Request, res: Response) => {
+ /*
+ #swagger.summary = 'Return secret versions'
+ #swagger.description = 'Return secret versions'
+
+ #swagger.security = [{
+ "apiKeyAuth": []
+ }]
+
+ #swagger.parameters['secretId'] = {
+ "description": "ID of secret",
+ "required": true,
+ "type": "string"
+ }
+
+ #swagger.parameters['offset'] = {
+ "description": "Number of versions to skip",
+ "required": false,
+ "type": "string"
+ }
+
+ #swagger.parameters['limit'] = {
+ "description": "Maximum number of versions to return",
+ "required": false,
+ "type": "string"
+ }
+
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ "type": "object",
+ "properties": {
+ "secretVersions": {
+ "type": "array",
+ "items": {
+ $ref: "#/components/schemas/SecretVersion"
+ },
+ "description": "Secret versions"
+ }
+ }
+ }
+ }
+ }
+ }
+ */
let secretVersions;
try {
const { secretId } = req.params;
@@ -44,6 +89,54 @@ import { EESecretService } from '../../services';
* @returns
*/
export const rollbackSecretVersion = async (req: Request, res: Response) => {
+ /*
+ #swagger.summary = 'Roll back secret to a version.'
+ #swagger.description = 'Roll back secret to a version.'
+
+ #swagger.security = [{
+ "apiKeyAuth": []
+ }]
+
+ #swagger.parameters['secretId'] = {
+ "description": "ID of secret",
+ "required": true,
+ "type": "string"
+ }
+
+ #swagger.requestBody = {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "version": {
+ "type": "integer",
+ "description": "Version of secret to roll back to"
+ }
+ }
+ }
+ }
+ }
+ }
+
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ "type": "object",
+ "properties": {
+ "secret": {
+ "type": "object",
+ $ref: "#/components/schemas/Secret",
+ "description": "Secret rolled back to"
+ }
+ }
+ }
+ }
+ }
+ }
+ */
let secret;
try {
const { secretId } = req.params;
diff --git a/backend/src/ee/controllers/v1/workspaceController.ts b/backend/src/ee/controllers/v1/workspaceController.ts
index 8fd7c8746..ea9bb7dab 100644
--- a/backend/src/ee/controllers/v1/workspaceController.ts
+++ b/backend/src/ee/controllers/v1/workspaceController.ts
@@ -19,6 +19,51 @@ import { getLatestSecretVersionIds } from '../../helpers/secretVersion';
* @param res
*/
export const getWorkspaceSecretSnapshots = async (req: Request, res: Response) => {
+ /*
+ #swagger.summary = 'Return project secret snapshot ids'
+ #swagger.description = 'Return project secret snapshots ids'
+
+ #swagger.security = [{
+ "apiKeyAuth": []
+ }]
+
+ #swagger.parameters['workspaceId'] = {
+ "description": "ID of project",
+ "required": true,
+ "type": "string"
+ }
+
+ #swagger.parameters['offset'] = {
+ "description": "Number of secret snapshots to skip",
+ "required": false,
+ "type": "string"
+ }
+
+ #swagger.parameters['limit'] = {
+ "description": "Maximum number of secret snapshots to return",
+ "required": false,
+ "type": "string"
+ }
+
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ "type": "object",
+ "properties": {
+ "secretSnapshots": {
+ "type": "array",
+ "items": {
+ $ref: "#/components/schemas/SecretSnapshot"
+ },
+ "description": "Project secret snapshots"
+ }
+ }
+ }
+ }
+ }
+ }
+ */
let secretSnapshots;
try {
const { workspaceId } = req.params;
@@ -78,16 +123,66 @@ export const getWorkspaceSecretSnapshotsCount = async (req: Request, res: Respon
* @returns
*/
export const rollbackWorkspaceSecretSnapshot = async (req: Request, res: Response) => {
+ /*
+ #swagger.summary = 'Roll back project secrets to those captured in a secret snapshot version.'
+ #swagger.description = 'Roll back project secrets to those captured in a secret snapshot version.'
+
+ #swagger.security = [{
+ "apiKeyAuth": []
+ }]
+
+ #swagger.parameters['workspaceId'] = {
+ "description": "ID of project",
+ "required": true,
+ "type": "string"
+ }
+
+ #swagger.requestBody = {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "version": {
+ "type": "integer",
+ "description": "Version of secret snapshot to roll back to",
+ }
+ }
+ }
+ }
+ }
+ }
+
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ "type": "object",
+ "properties": {
+ "secrets": {
+ "type": "array",
+ "items": {
+ $ref: "#/components/schemas/Secret"
+ },
+ "description": "Secrets rolled back to"
+ }
+ }
+ }
+ }
+ }
+ }
+ */
let secrets;
try {
const { workspaceId } = req.params;
const { version } = req.body;
// validate secret snapshot
- const secretSnapshot = await SecretSnapshot.findOne({
- workspace: workspaceId,
- version
- }).populate<{ secretVersions: ISecretVersion[]}>('secretVersions');
+ const secretSnapshot = await SecretSnapshot.findOne({
+ workspace: workspaceId,
+ version
+ }).populate<{ secretVersions: ISecretVersion[]}>('secretVersions');
if (!secretSnapshot) throw new Error('Failed to find secret snapshot');
@@ -231,6 +326,72 @@ export const rollbackWorkspaceSecretSnapshot = async (req: Request, res: Respons
* @returns
*/
export const getWorkspaceLogs = async (req: Request, res: Response) => {
+ /*
+ #swagger.summary = 'Return project (audit) logs'
+ #swagger.description = 'Return project (audit) logs'
+
+ #swagger.security = [{
+ "apiKeyAuth": []
+ }]
+
+ #swagger.parameters['workspaceId'] = {
+ "description": "ID of project",
+ "required": true,
+ "type": "string"
+ }
+
+ #swagger.parameters['userId'] = {
+ "description": "ID of project member",
+ "required": false,
+ "type": "string"
+ }
+
+ #swagger.parameters['offset'] = {
+ "description": "Number of logs to skip",
+ "required": false,
+ "type": "string"
+ }
+
+ #swagger.parameters['limit'] = {
+ "description": "Maximum number of logs to return",
+ "required": false,
+ "type": "string"
+ }
+
+ #swagger.parameters['sortBy'] = {
+ "description": "Order to sort the logs by",
+ "schema": {
+ "type": "string",
+ "@enum": ["oldest", "recent"]
+ },
+ "required": false
+ }
+
+ #swagger.parameters['actionNames'] = {
+ "description": "Names of log actions (comma-separated)",
+ "required": false,
+ "type": "string"
+ }
+
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ "type": "object",
+ "properties": {
+ "logs": {
+ "type": "array",
+ "items": {
+ $ref: "#/components/schemas/Log"
+ },
+ "description": "Project logs"
+ }
+ }
+ }
+ }
+ }
+ }
+ */
let logs
try {
const { workspaceId } = req.params;
diff --git a/backend/src/middleware/index.ts b/backend/src/middleware/index.ts
index 6a3537076..e9bffac2d 100644
--- a/backend/src/middleware/index.ts
+++ b/backend/src/middleware/index.ts
@@ -2,6 +2,7 @@ import requireAuth from './requireAuth';
import requireBotAuth from './requireBotAuth';
import requireSignupAuth from './requireSignupAuth';
import requireWorkspaceAuth from './requireWorkspaceAuth';
+import requireMembershipAuth from './requireMembershipAuth';
import requireOrganizationAuth from './requireOrganizationAuth';
import requireIntegrationAuth from './requireIntegrationAuth';
import requireIntegrationAuthorizationAuth from './requireIntegrationAuthorizationAuth';
@@ -16,6 +17,7 @@ export {
requireBotAuth,
requireSignupAuth,
requireWorkspaceAuth,
+ requireMembershipAuth,
requireOrganizationAuth,
requireIntegrationAuth,
requireIntegrationAuthorizationAuth,
diff --git a/backend/src/middleware/requireMembershipAuth.ts b/backend/src/middleware/requireMembershipAuth.ts
new file mode 100644
index 000000000..06c0980a3
--- /dev/null
+++ b/backend/src/middleware/requireMembershipAuth.ts
@@ -0,0 +1,58 @@
+import { Request, Response, NextFunction } from 'express';
+import { UnauthorizedRequestError } from '../utils/errors';
+import {
+ Membership,
+} from '../models';
+import { validateMembership } from '../helpers/membership';
+
+type req = 'params' | 'body' | 'query';
+/**
+ * Validate membership with id [membershipId] and that user with id
+ * [req.user._id] can modify that membership.
+ * @param {Object} obj
+ * @param {String[]} obj.acceptedRoles - accepted workspace roles for JWT auth
+ * @param {String[]} obj.location - location of [workspaceId] on request (e.g. params, body) for parsing
+ */
+const requireMembershipAuth = ({
+ acceptedRoles,
+ location = 'params'
+}: {
+ acceptedRoles: string[];
+ location?: req;
+}) => {
+ return async (
+ req: Request,
+ res: Response,
+ next: NextFunction
+ ) => {
+ try {
+ const { membershipId } = req[location];
+
+ const membership = await Membership.findById(membershipId);
+
+ if (!membership) throw new Error('Failed to find target membership');
+
+ const userMembership = await Membership.findOne({
+ workspace: membership.workspace
+ });
+
+ if (!userMembership) throw new Error('Failed to validate own membership')
+
+ const targetMembership = await validateMembership({
+ userId: req.user._id.toString(),
+ workspaceId: membership.workspace.toString(),
+ acceptedRoles
+ });
+
+ req.targetMembership = targetMembership;
+
+ return next();
+ } catch (err) {
+ return next(UnauthorizedRequestError({
+ message: 'Unable to validate workspace membership'
+ }));
+ }
+ }
+}
+
+export default requireMembershipAuth;
\ No newline at end of file
diff --git a/backend/src/routes/v1/membership.ts b/backend/src/routes/v1/membership.ts
index 76bf3e57a..f2c3eb554 100644
--- a/backend/src/routes/v1/membership.ts
+++ b/backend/src/routes/v1/membership.ts
@@ -4,7 +4,7 @@ import { body, param } from 'express-validator';
import { requireAuth, validateRequest } from '../../middleware';
import { membershipController } from '../../controllers/v1';
-router.get( // used for CLI (deprecate)
+router.get( // used for old CLI (deprecate)
'/:workspaceId/connect',
requireAuth({
acceptedAuthModes: ['jwt']
diff --git a/backend/src/routes/v2/index.ts b/backend/src/routes/v2/index.ts
index 2740c3530..cc9375fb7 100644
--- a/backend/src/routes/v2/index.ts
+++ b/backend/src/routes/v2/index.ts
@@ -1,3 +1,4 @@
+import users from './users';
import secret from './secret'; // stop-supporting
import secrets from './secrets';
import workspace from './workspace';
@@ -6,6 +7,7 @@ import apiKeyData from './apiKeyData';
import environment from "./environment"
export {
+ users,
secret,
secrets,
workspace,
diff --git a/backend/src/routes/v2/users.ts b/backend/src/routes/v2/users.ts
new file mode 100644
index 000000000..dba107b15
--- /dev/null
+++ b/backend/src/routes/v2/users.ts
@@ -0,0 +1,16 @@
+import express from 'express';
+const router = express.Router();
+import {
+ requireAuth
+} from '../../middleware';
+import { usersController } from '../../controllers/v2';
+
+router.get(
+ '/me',
+ requireAuth({
+ acceptedAuthModes: ['jwt']
+ }),
+ usersController.getMe
+);
+
+export default router;
\ No newline at end of file
diff --git a/backend/src/routes/v2/workspace.ts b/backend/src/routes/v2/workspace.ts
index c90834d6d..ca920e15a 100644
--- a/backend/src/routes/v2/workspace.ts
+++ b/backend/src/routes/v2/workspace.ts
@@ -3,6 +3,7 @@ const router = express.Router();
import { body, param, query } from 'express-validator';
import {
requireAuth,
+ requireMembershipAuth,
requireWorkspaceAuth,
validateRequest
} from '../../middleware';
@@ -67,4 +68,54 @@ router.get(
workspaceController.getWorkspaceServiceTokenData
);
+// TODO: /POST to create membership and re-route inviting user to workspace there
+
+router.get( // new - TODO: rewire dashboard to this route
+ '/:workspaceId/memberships',
+ param('workspaceId').exists().trim(),
+ validateRequest,
+ requireAuth({
+ acceptedAuthModes: ['jwt']
+ }),
+ requireWorkspaceAuth({
+ acceptedRoles: [ADMIN, MEMBER],
+ }),
+ workspaceController.getWorkspaceMemberships
+);
+
+router.delete( // TODO - rewire dashboard to this route
+ '/:workspaceId/memberships/:membershipId',
+ param('workspaceId').exists().trim(),
+ param('membershipId').exists().trim(),
+ validateRequest,
+ requireAuth({
+ acceptedAuthModes: ['jwt']
+ }),
+ requireWorkspaceAuth({
+ acceptedRoles: [ADMIN],
+ }),
+ requireMembershipAuth({
+ acceptedRoles: [ADMIN]
+ }),
+ workspaceController.deleteWorkspaceMembership
+);
+
+router.patch( // TODO - rewire dashboard to this route
+ '/:workspaceId/memberships/:membershipId',
+ param('workspaceId').exists().trim(),
+ param('membershipId').exists().trim(),
+ body('role').exists().isString().trim().isIn([ADMIN, MEMBER]),
+ validateRequest,
+ requireAuth({
+ acceptedAuthModes: ['jwt']
+ }),
+ requireWorkspaceAuth({
+ acceptedRoles: [ADMIN],
+ }),
+ requireMembershipAuth({
+ acceptedRoles: [ADMIN]
+ }),
+ workspaceController.updateWorkspaceMembership
+);
+
export default router;
diff --git a/backend/src/types/express/index.d.ts b/backend/src/types/express/index.d.ts
index ae9edb4c5..1ce63e609 100644
--- a/backend/src/types/express/index.d.ts
+++ b/backend/src/types/express/index.d.ts
@@ -8,6 +8,7 @@ declare global {
user: any;
workspace: any;
membership: any;
+ targetMembership: any;
organization: any;
membershipOrg: any;
integration: any;
diff --git a/backend/swagger.ts b/backend/swagger.ts
deleted file mode 100644
index 0505bb813..000000000
--- a/backend/swagger.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-// eslint-disable-next-line @typescript-eslint/no-var-requires
-const swaggerAutogen = require('swagger-autogen')({ openapi: '3.0.0' });
-
-const doc = {
- info: {
- title: 'Infisical API',
- description: 'List of all available APIs that can be consumed',
- },
- host: ['https://infisical.com'],
- securityDefinitions: {
- bearerAuth: {
- type: 'http',
- scheme: 'bearer',
- bearerFormat: 'JWT'
- }
- }
-};
-
-const outputFile = './api-documentation.json';
-const endpointsFiles = ['./src/app.ts'];
-
-swaggerAutogen(outputFile, endpointsFiles, doc);
\ No newline at end of file
diff --git a/backend/swagger/index.ts b/backend/swagger/index.ts
new file mode 100644
index 000000000..cbf05ed7e
--- /dev/null
+++ b/backend/swagger/index.ts
@@ -0,0 +1,186 @@
+/* eslint-disable @typescript-eslint/no-var-requires */
+const swaggerAutogen = require('swagger-autogen')({ openapi: '3.0.0' });
+const fs = require('fs').promises;
+const yaml = require('js-yaml');
+const { secretSchema } = require('./schemas/index.ts');
+
+/**
+ * Generates OpenAPI specs for all Infisical API endpoints:
+ * - spec.json in /backend for api-serving
+ * - spec.yaml in /docs for API reference
+ */
+const generateOpenAPISpec = async () => {
+ const doc = {
+ info: {
+ title: 'Infisical API',
+ description: 'List of all available APIs that can be consumed',
+ },
+ host: ['https://infisical.com'],
+ servers: [
+ {
+ url: 'https://infisical.com',
+ description: 'Production server'
+ },
+ {
+ url: 'http://localhost:8080',
+ description: 'Local server'
+ }
+ ],
+ securityDefinitions: {
+ bearerAuth: {
+ type: 'http',
+ scheme: 'bearer',
+ bearerFormat: 'JWT',
+ description: "This security definition uses the HTTP 'bearer' scheme, which allows the client to authenticate using a JSON Web Token (JWT) that is passed in the Authorization header of the request."
+ },
+ apiKeyAuth: {
+ type: 'apiKey',
+ in: 'header',
+ name: 'X-API-Key',
+ description: 'This security definition uses an API key, which is passed in the header of the request as the value of the "X-API-Key" header. The client must provide a valid key in order to access the API.'
+ }
+ },
+ definitions: {
+ CurrentUser: {
+ _id: '',
+ email: '',
+ firstName: '',
+ lastName: '',
+ publicKey: '',
+ encryptedPrivateKey: '',
+ iv: '',
+ tag: '',
+ updatedAt: '',
+ createdAt: ''
+ },
+ Membership: {
+ user: {
+ _id: '',
+ email: '',
+ firstName: '',
+ lastName: '',
+ publicKey: '',
+ updatedAt: '',
+ createdAt: ''
+ },
+ workspace: '',
+ role: 'admin'
+ },
+ ProjectKey: {
+ encryptedkey: '',
+ nonce: '',
+ sender: {
+ publicKey: ''
+ },
+ receiver: '',
+ workspace: ''
+ },
+ CreateSecret: {
+ type: 'shared',
+ secretKeyCiphertext: '',
+ secretKeyIV: '',
+ secretKeyTag: '',
+ secretValueCiphertext: '',
+ secretValueIV: '',
+ secretValueTag: '',
+ secretCommentCiphertext: '',
+ secretCommentIV: '',
+ secretCommentTag: ''
+ },
+ UpdateSecret: {
+ id: '',
+ secretKeyCiphertext: '',
+ secretKeyIV: '',
+ secretKeyTag: '',
+ secretValueCiphertext: '',
+ secretValueIV: '',
+ secretValueTag: '',
+ secretCommentCiphertext: '',
+ secretCommentIV: '',
+ secretCommentTag: ''
+ },
+ Secret: {
+ _id: '',
+ version: 1,
+ workspace : '',
+ type: 'shared',
+ user: null,
+ secretKeyCiphertext: '',
+ secretKeyIV: '',
+ secretKeyTag: '',
+ secretValueCiphertext: '',
+ secretValueIV: '',
+ secretValueTag: '',
+ secretCommentCiphertext: '',
+ secretCommentIV: '',
+ secretCommentTag: '',
+ updatedAt: '',
+ createdAt: ''
+ },
+ Log: {
+ _id: '',
+ user: {
+ _id: '',
+ email: '',
+ firstName: '',
+ lastName: ''
+ },
+ workspace: '',
+ actionNames: [
+ 'addSecrets'
+ ],
+ actions: [
+ {
+ name: 'addSecrets',
+ user: '',
+ workspace: '',
+ payload: [
+ {
+ oldSecretVersion: '',
+ newSecretVersion: ''
+ }
+ ]
+ }
+ ],
+ channel: 'cli',
+ ipAddress: '192.168.0.1',
+ updatedAt: '',
+ createdAt: ''
+ },
+ SecretSnapshot: {
+ workspace: '',
+ version: 1,
+ secretVersions: [
+ {
+ _id: ''
+ }
+ ]
+ },
+ SecretVersion: {
+ _id: '',
+ secret: '',
+ version: 1,
+ workspace: '',
+ type: '',
+ user: '',
+ environment: '',
+ isDeleted: '',
+ secretKeyCiphertext: '',
+ secretKeyIV: '',
+ secretKeyTag: '',
+ secretValueCiphertext: '',
+ secretValueIV: '',
+ secretValueTag: '',
+ }
+ }
+ };
+
+ const outputJSONFile = '../spec.json';
+ const outputYAMLFile = '../docs/spec.yaml';
+ const endpointsFiles = ['../src/app.ts'];
+
+ const spec = await swaggerAutogen(outputJSONFile, endpointsFiles, doc);
+ await fs.writeFile(outputYAMLFile, yaml.dump(spec.data));
+}
+
+generateOpenAPISpec();
diff --git a/backend/swagger/schemas/index.ts b/backend/swagger/schemas/index.ts
new file mode 100644
index 000000000..b0b427fce
--- /dev/null
+++ b/backend/swagger/schemas/index.ts
@@ -0,0 +1,6 @@
+/* eslint-disable @typescript-eslint/no-var-requires */
+const secretSchema = require('./secretSchema.ts');
+
+module.exports = {
+ secretSchema
+}
\ No newline at end of file
diff --git a/backend/swagger/schemas/secretSchema.ts b/backend/swagger/schemas/secretSchema.ts
new file mode 100644
index 000000000..85c6ce017
--- /dev/null
+++ b/backend/swagger/schemas/secretSchema.ts
@@ -0,0 +1,11 @@
+const secretSchema = {
+ _id: {
+ type: 'string',
+ format: 'objectId'
+ },
+ version: {
+ type: 'number'
+ }
+}
+
+module.exports = secretSchema;
\ No newline at end of file
diff --git a/docs/api-reference/endpoints/secrets/read.mdx b/docs/api-reference/endpoints/secrets/read.mdx
index 4305f192c..c88fb0ec7 100644
--- a/docs/api-reference/endpoints/secrets/read.mdx
+++ b/docs/api-reference/endpoints/secrets/read.mdx
@@ -1,4 +1,4 @@
---
-title: "Read"
+title: "Retrieve"
openapi: "GET /api/v2/secrets/"
---
diff --git a/docs/api-reference/endpoints/secrets/rollback-version.mdx b/docs/api-reference/endpoints/secrets/rollback-version.mdx
new file mode 100644
index 000000000..dff577fe1
--- /dev/null
+++ b/docs/api-reference/endpoints/secrets/rollback-version.mdx
@@ -0,0 +1,4 @@
+---
+title: "Roll Back to Version"
+openapi: "POST /api/v1/secret/{secretId}/secret-versions/rollback"
+---
diff --git a/docs/api-reference/endpoints/secrets/versions.mdx b/docs/api-reference/endpoints/secrets/versions.mdx
new file mode 100644
index 000000000..e8693684c
--- /dev/null
+++ b/docs/api-reference/endpoints/secrets/versions.mdx
@@ -0,0 +1,4 @@
+---
+title: "Get Versions"
+openapi: "GET /api/v1/secret/{secretId}/secret-versions"
+---
diff --git a/docs/api-reference/endpoints/users/me.mdx b/docs/api-reference/endpoints/users/me.mdx
new file mode 100644
index 000000000..9273f0ca6
--- /dev/null
+++ b/docs/api-reference/endpoints/users/me.mdx
@@ -0,0 +1,4 @@
+---
+title: "Get Current User"
+openapi: "GET /api/v2/users/me"
+---
diff --git a/docs/api-reference/endpoints/workspaces/delete-membership.mdx b/docs/api-reference/endpoints/workspaces/delete-membership.mdx
new file mode 100644
index 000000000..826eafa83
--- /dev/null
+++ b/docs/api-reference/endpoints/workspaces/delete-membership.mdx
@@ -0,0 +1,4 @@
+---
+title: "Delete Membership"
+openapi: "DELETE /api/v2/workspace/{workspaceId}/memberships/{membershipId}"
+---
diff --git a/docs/api-reference/endpoints/workspaces/logs.mdx b/docs/api-reference/endpoints/workspaces/logs.mdx
new file mode 100644
index 000000000..d5921c1c0
--- /dev/null
+++ b/docs/api-reference/endpoints/workspaces/logs.mdx
@@ -0,0 +1,4 @@
+---
+title: "Get Logs"
+openapi: "GET /api/v1/workspace/{workspaceId}/logs"
+---
diff --git a/docs/api-reference/endpoints/workspaces/memberships.mdx b/docs/api-reference/endpoints/workspaces/memberships.mdx
new file mode 100644
index 000000000..38b86d616
--- /dev/null
+++ b/docs/api-reference/endpoints/workspaces/memberships.mdx
@@ -0,0 +1,4 @@
+---
+title: "Get Memberships"
+openapi: "GET /api/v2/workspace/{workspaceId}/memberships"
+---
diff --git a/docs/api-reference/endpoints/workspaces/rollback-snapshot.mdx b/docs/api-reference/endpoints/workspaces/rollback-snapshot.mdx
new file mode 100644
index 000000000..8b648a400
--- /dev/null
+++ b/docs/api-reference/endpoints/workspaces/rollback-snapshot.mdx
@@ -0,0 +1,4 @@
+---
+title: "Roll Back to Snapshot"
+openapi: "POST /api/v1/workspace/{workspaceId}/secret-snapshots/rollback"
+---
diff --git a/docs/api-reference/endpoints/workspaces/secret-snapshots.mdx b/docs/api-reference/endpoints/workspaces/secret-snapshots.mdx
new file mode 100644
index 000000000..2d645d4d0
--- /dev/null
+++ b/docs/api-reference/endpoints/workspaces/secret-snapshots.mdx
@@ -0,0 +1,4 @@
+---
+title: "Get Snapshots"
+openapi: "GET /api/v1/workspace/{workspaceId}/secret-snapshots"
+---
diff --git a/docs/api-reference/endpoints/workspaces/update-membership.mdx b/docs/api-reference/endpoints/workspaces/update-membership.mdx
new file mode 100644
index 000000000..367793847
--- /dev/null
+++ b/docs/api-reference/endpoints/workspaces/update-membership.mdx
@@ -0,0 +1,4 @@
+---
+title: "Update Membership"
+openapi: "PATCH /api/v2/workspace/{workspaceId}/memberships/{membershipId}"
+---
diff --git a/docs/api-reference/endpoints/workspaces/workspace-key.mdx b/docs/api-reference/endpoints/workspaces/workspace-key.mdx
new file mode 100644
index 000000000..05313eb8f
--- /dev/null
+++ b/docs/api-reference/endpoints/workspaces/workspace-key.mdx
@@ -0,0 +1,4 @@
+---
+title: "Get Key"
+openapi: "GET /api/v2/workspace/{workspaceId}/encrypted-key"
+---
diff --git a/docs/api-reference/overview/authentication.mdx b/docs/api-reference/overview/authentication.mdx
index 27a2dc134..8cc218253 100644
--- a/docs/api-reference/overview/authentication.mdx
+++ b/docs/api-reference/overview/authentication.mdx
@@ -1,3 +1,11 @@
---
title: "Authentication"
---
+
+To authenticate requests with Infisical, you must include an API key in the `X-API-KEY` header of HTTP requests made to the platform. You can obtain an API key from your user settings.
+
+
+ It's important to keep your API key secure, as it grants access to your
+ secrets in Infisical. For added security, consider rotating your API key on a
+ regular basis.
+
diff --git a/docs/api-reference/overview/examples/create-secrets.mdx b/docs/api-reference/overview/examples/create-secrets.mdx
new file mode 100644
index 000000000..b2afe1467
--- /dev/null
+++ b/docs/api-reference/overview/examples/create-secrets.mdx
@@ -0,0 +1,152 @@
+---
+title: "Create secrets"
+---
+
+In this example, we demonstrate how to add secrets to a project and environment.
+
+Prerequisites:
+
+- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
+- Grasp a basic understanding of the system and its underlying cryptography [here](/api-reference/overview/introduction).
+
+## Flow
+
+1. Get your (encrypted) private key.
+2. Decrypt your (encrypted) private key with your password.
+3. Get the (encrypted) project key for the project.
+4. Decrypt the (encrypted) project key with your private key.
+5. Encrypt your secret(s) with the project key.
+6. Send (encrypted) secret(s) to the Infical API
+
+## Example
+
+```js
+const crypto = require('crypto');
+const axios = require('axios');
+
+const ALGORITHM = 'aes-256-gcm';
+const BLOCK_SIZE_BYTES = 16;
+
+const encrypt = (
+ text,
+ secret
+) => {
+ const iv = crypto.randomBytes(BLOCK_SIZE_BYTES);
+ const cipher = crypto.createCipheriv(ALGORITHM, secret, iv);
+
+ let ciphertext = cipher.update(text, 'utf8', 'base64');
+ ciphertext += cipher.final('base64');
+ return {
+ ciphertext,
+ iv: iv.toString('base64'),
+ tag: cipher.getAuthTag().toString('base64')
+ };
+}
+
+const decrypt = (ciphertext, iv, tag, secret) => {
+ const decipher = crypto.createDecipheriv(
+ ALGORITHM,
+ secret,
+ Buffer.from(iv, 'base64')
+ );
+ decipher.setAuthTag(Buffer.from(tag, 'base64'));
+
+ let cleartext = decipher.update(ciphertext, 'base64', 'utf8');
+ cleartext += decipher.final('utf8');
+
+ return cleartext;
+}
+
+const createSecrets = async () => {
+ const API_KEY = 'your_api_key';
+ const PSWD = 'your_pswd';
+ const WORKSPACE_ID = 'your_workspace_id';
+
+ const SECRET_KEY = 'SOME_KEY';
+ const SECRET_VALUE = 'SOME_VALUE';
+
+ // 1. get (encrypted) private key
+ const user = await axios.get(
+ 'https://api.infisical.com/api/v2/users/me', {
+ headers: {
+ 'X-API-KEY': API_KEY
+ }
+ }
+ );
+
+ // 2. decrypt your (encrypted) private key with your password
+ const privateKey = decrypt({
+ ciphertext: user.encryptedPrivateKey,
+ iv: user.iv,
+ tag: user.tag,
+ secret: PSWD.slice(0, 32).padStart(32, '0');
+ });
+
+ // 3. get the (encrypted) project key for the project
+ const encryptedProjectKey = await axios.get(
+ `https://api.infisical.com/api/v2/workspace/${WORKSPACE_ID}`, {
+ headers: {
+ 'X-API-KEY': API_KEY
+ }
+ }
+ );
+
+ // 4. decrypt the project key with your private key
+ const projectKey = nacl.box.open(
+ util.decodeBase64(encryptedProjectKey),
+ util.decodeBase64(projectKey.nonce),
+ util.decodeBase64(projectKey.sender.publicKey),
+ util.decodeBase64(privateKey)
+ );
+
+ // 5. encrypt your secret(s) with the project key
+ const {
+ ciphertext: secretKeyCiphertext,
+ iv: secretKeyIV,
+ tag: secretKeyTag
+ } = encrypt(SECRET_KEY, projectKey);
+
+ const {
+ ciphertext: secretValueCiphertext,
+ iv: secretValueIV,
+ tag: secretValueTag
+ } = encrypt(SECRET_VALUE, projectKey);
+
+ const secret = {
+ secretKeyCiphertext,
+ secretKeyIV,
+ secretKeyTag,
+ secretValueCiphertext,
+ secretValueIV,
+ secretValueTag
+ }
+
+ // 6. Send (encrypted) secret(s) to the Infisical API
+ await axios.post(
+ `https://api.infisical.com/api/v2/secrets`,
+ {
+ workspaceId: WORKSPACE_ID,
+ environment: 'dev',
+ secrets: secret
+ },
+ {
+ headers: {
+ 'X-API-KEY': API_KEY
+ }
+ }
+ );
+}
+
+createSecrets();
+```
+
+
+ This example uses [TweetNaCl.js](https://tweetnacl.js.org/#/), a port of
+ TweetNacl/Nacl, to perform asymmeric decryption of the project key but there
+ are ports of NaCl available in every major language.
+
+
+ It can be useful to perform steps 1-4 ahead of time and store away your
+ private key (and even project key) for later use. The Infisical CLI works by
+ securely storing your private key via your OS keyring.
+
diff --git a/docs/api-reference/overview/examples/delete-secrets.mdx b/docs/api-reference/overview/examples/delete-secrets.mdx
new file mode 100644
index 000000000..c602cef7e
--- /dev/null
+++ b/docs/api-reference/overview/examples/delete-secrets.mdx
@@ -0,0 +1,34 @@
+---
+title: "Delete secrets"
+---
+
+In this example, we demonstrate how to delete secrets
+
+Prerequisites:
+
+- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
+- Grasp a basic understanding of the system and its underlying cryptography [here](/api-reference/overview/introduction).
+
+## Example
+
+```js
+const deleteSecrets = async () => {
+ const API_KEY = "your_api_key";
+ const SECRET_ID = "ID"; // ID of secret to delete
+
+ // 6. Send ID(s) of secret(s) to delete to the Infisical API
+ await axios.delete(
+ `https://api.infisical.com/api/v2/secrets`,
+ {
+ secretIds: SECRET_ID,
+ },
+ {
+ headers: {
+ "X-API-KEY": API_KEY,
+ },
+ }
+ );
+};
+
+deleteSecrets();
+```
diff --git a/docs/api-reference/overview/examples/retrieve-secrets.mdx b/docs/api-reference/overview/examples/retrieve-secrets.mdx
new file mode 100644
index 000000000..2f34339c5
--- /dev/null
+++ b/docs/api-reference/overview/examples/retrieve-secrets.mdx
@@ -0,0 +1,142 @@
+---
+title: "Retrieve secrets"
+---
+
+In this example, we demonstrate how to retrieve secrets from a project and environment.
+
+Prerequisites:
+
+- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
+- Grasp a basic understanding of the system and its underlying cryptography [here](/api-reference/overview/introduction).
+
+## Flow
+
+1. Get your (encrypted) private key.
+2. Decrypt your (encrypted) private key with your password.
+3. Get the (encrypted) project key for the project.
+4. Decrypt the (encrypted) project key with your private key.
+5. Get secrets for a project and environment.
+6. Decrypt the (encrypted) secrets
+
+## Example
+
+```js
+const crypto = require('crypto');
+const axios = require('axios');
+
+const ALGORITHM = 'aes-256-gcm';
+const BLOCK_SIZE_BYTES = 16;
+
+const encrypt = (
+ text,
+ secret
+) => {
+ const iv = crypto.randomBytes(BLOCK_SIZE_BYTES);
+ const cipher = crypto.createCipheriv(ALGORITHM, secret, iv);
+
+ let ciphertext = cipher.update(text, 'utf8', 'base64');
+ ciphertext += cipher.final('base64');
+ return {
+ ciphertext,
+ iv: iv.toString('base64'),
+ tag: cipher.getAuthTag().toString('base64')
+ };
+}
+
+const decrypt = (ciphertext, iv, tag, secret) => {
+ const decipher = crypto.createDecipheriv(
+ ALGORITHM,
+ secret,
+ Buffer.from(iv, 'base64')
+ );
+ decipher.setAuthTag(Buffer.from(tag, 'base64'));
+
+ let cleartext = decipher.update(ciphertext, 'base64', 'utf8');
+ cleartext += decipher.final('utf8');
+
+ return cleartext;
+}
+
+const retrieveSecrets = async () => {
+ const API_KEY = 'your_api_key';
+ const PSWD = 'your_pswd';
+ const WORKSPACE_ID = 'your_workspace_id';
+
+ // 1. get (encrypted) private key
+ const user = await axios.get(
+ 'https://api.infisical.com/api/v2/users/me', {
+ headers: {
+ 'X-API-KEY': API_KEY
+ }
+ }
+ );
+
+ // 2. decrypt your (encrypted) private key with your password
+ const privateKey = decrypt({
+ ciphertext: user.encryptedPrivateKey,
+ iv: user.iv,
+ tag: user.tag,
+ secret: PSWD.slice(0, 32).padStart(32, '0');
+ });
+
+ // 3. get the (encrypted) project key for the project
+ const encryptedProjectKey = await axios.get(
+ `https://api.infisical.com/api/v2/workspace/${WORKSPACE_ID}`, {
+ headers: {
+ 'X-API-KEY': API_KEY
+ }
+ }
+ );
+
+ // 4. decrypt the project key with your private key
+ const projectKey = nacl.box.open(
+ util.decodeBase64(encryptedProjectKey),
+ util.decodeBase64(projectKey.nonce),
+ util.decodeBase64(projectKey.sender.publicKey),
+ util.decodeBase64(privateKey)
+ );
+
+ // 5. get (encrypted) secrets for a project and environment.
+ const encryptedSecrets = await axios.get(
+ 'https://api.infisical.com/api/v2/secrets', {
+ headers: {
+ 'X-API-KEY': API_KEY
+ }
+ }
+ );
+
+ // 6. decrypt the (encrypted) secrets
+ const secrets = encryptedSecrets.map((encryptedSecret) => {
+ const secretKey = decrypt({
+ ciphertext: encryptedSecret.secretKeyCiphertext,
+ iv: encryptedSecret.secretKeyIV,
+ tag: encryptedSecret.secretKeyTag
+ secret: projectKey
+ });
+ const secretValue = decrypt({
+ ciphertext: encryptedSecret.secretValueCiphertext,
+ iv: encryptedSecret.secretValueIV,
+ tag: encryptedSecret.secretValueTag
+ secret: projectKey
+ });
+
+ return ({
+ secretKey,
+ secretValue
+ });
+ });
+}
+
+retrieveSecrets();
+```
+
+
+ This example uses [TweetNaCl.js](https://tweetnacl.js.org/#/), a port of
+ TweetNacl/Nacl, to perform asymmeric decryption of the project key but there
+ are ports of NaCl available in every major language.
+
+
+ It can be useful to perform steps 1-4 ahead of time and store away your
+ private key (and even project key) for later use. The Infisical CLI works by
+ securely storing your private key via your OS keyring.
+
diff --git a/docs/api-reference/overview/examples/update-secrets.mdx b/docs/api-reference/overview/examples/update-secrets.mdx
new file mode 100644
index 000000000..b8566d9b3
--- /dev/null
+++ b/docs/api-reference/overview/examples/update-secrets.mdx
@@ -0,0 +1,152 @@
+---
+title: "Update secrets"
+---
+
+In this example, we demonstrate how to update secrets
+
+Prerequisites:
+
+- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
+- Grasp a basic understanding of the system and its underlying cryptography [here](/api-reference/overview/introduction).
+
+## Flow
+
+1. Get your (encrypted) private key.
+2. Decrypt your (encrypted) private key with your password.
+3. Get the project key for the project.
+4. Decrypt the project key with your private key.
+5. Encrypt your secret(s) with the project key.
+6. Send (encrypted) updated secret(s) to the Infical API
+
+## Example
+
+```js
+const crypto = require('crypto');
+const axios = require('axios');
+
+const ALGORITHM = 'aes-256-gcm';
+const BLOCK_SIZE_BYTES = 16;
+
+const encrypt = (
+ text,
+ secret
+) => {
+ const iv = crypto.randomBytes(BLOCK_SIZE_BYTES);
+ const cipher = crypto.createCipheriv(ALGORITHM, secret, iv);
+
+ let ciphertext = cipher.update(text, 'utf8', 'base64');
+ ciphertext += cipher.final('base64');
+ return {
+ ciphertext,
+ iv: iv.toString('base64'),
+ tag: cipher.getAuthTag().toString('base64')
+ };
+}
+
+const decrypt = (ciphertext, iv, tag, secret) => {
+ const decipher = crypto.createDecipheriv(
+ ALGORITHM,
+ secret,
+ Buffer.from(iv, 'base64')
+ );
+ decipher.setAuthTag(Buffer.from(tag, 'base64'));
+
+ let cleartext = decipher.update(ciphertext, 'base64', 'utf8');
+ cleartext += decipher.final('utf8');
+
+ return cleartext;
+}
+
+const updateSecrets = async () => {
+ const API_KEY = 'your_api_key';
+ const PSWD = 'your_pswd';
+ const WORKSPACE_ID = 'your_workspace_id';
+
+ const SECRET_ID = 'ID' // ID of secret to update
+ const SECRET_KEY = 'SOME_KEY';
+ const SECRET_VALUE = 'SOME_VALUE';
+
+ // 1. get (encrypted) private key
+ const user = await axios.get(
+ 'https://api.infisical.com/api/v2/users/me', {
+ headers: {
+ 'X-API-KEY': API_KEY
+ }
+ }
+ );
+
+ // 2. decrypt your (encrypted) private key with your password
+ const privateKey = decrypt({
+ ciphertext: user.encryptedPrivateKey,
+ iv: user.iv,
+ tag: user.tag,
+ secret: PSWD.slice(0, 32).padStart(32, '0');
+ });
+
+ // 3. get the (encrypted) project key for the project
+ const encryptedProjectKey = await axios.get(
+ `https://api.infisical.com/api/v2/workspace/${WORKSPACE_ID}`, {
+ headers: {
+ 'X-API-KEY': API_KEY
+ }
+ }
+ );
+
+ // 4. decrypt the project key with your private key
+ const projectKey = nacl.box.open(
+ util.decodeBase64(encryptedProjectKey),
+ util.decodeBase64(projectKey.nonce),
+ util.decodeBase64(projectKey.sender.publicKey),
+ util.decodeBase64(privateKey)
+ );
+
+ // 5. encrypt your secret(s) with the project key
+ const {
+ ciphertext: secretKeyCiphertext,
+ iv: secretKeyIV,
+ tag: secretKeyTag
+ } = encrypt(SECRET_KEY, projectKey);
+
+ const {
+ ciphertext: secretValueCiphertext,
+ iv: secretValueIV,
+ tag: secretValueTag
+ } = encrypt(SECRET_VALUE, projectKey);
+
+ const secret = {
+ id: SECRET_ID,
+ secretKeyCiphertext,
+ secretKeyIV,
+ secretKeyTag,
+ secretValueCiphertext,
+ secretValueIV,
+ secretValueTag
+ }
+
+ // 6. Send (encrypted) secret(s) to the Infisical API
+ await axios.patch(
+ `https://api.infisical.com/api/v2/secrets`,
+ {
+ secrets: secret
+ },
+ {
+ headers: {
+ 'X-API-KEY': API_KEY
+ }
+ }
+ );
+}
+
+updateSecrets();
+```
+
+
+ This example uses [TweetNaCl.js](https://tweetnacl.js.org/#/), a port of
+ TweetNacl/Nacl, to perform asymmeric decryption of the project key but there
+ are ports of NaCl available in every major language.
+
+
+ It can be useful to perform steps 1-4 ahead of time and store away your
+ private key (and even project key) for later use. The Infisical CLI works by
+ securely storing your private key via your OS keyring.
+
diff --git a/docs/api-reference/overview/introduction.mdx b/docs/api-reference/overview/introduction.mdx
index 9632e3788..7babef955 100644
--- a/docs/api-reference/overview/introduction.mdx
+++ b/docs/api-reference/overview/introduction.mdx
@@ -1,3 +1,31 @@
---
title: "Introduction"
---
+
+
+ Infisical's REST API is currently unavailable and scheduled to go live on Jan
+ 16!
+
+
+Infisical's REST API provides users an alternative way to programmatically access and manage
+secrets via HTTPS requests. This can be useful for automating tasks, such as
+rotating credentials, or for integrating secret management into a larger system.
+
+With the REST API, users can create, read, update, and delete secrets, as well as manage access control, query audit logs, and more.
+
+## Concepts
+
+Using Infisical's API to manage secrets requires a basic understanding of the system and its underlying cryptography detailed [here](/security/overview).
+
+- Each user has a public/private key pair that is stored with the platform; private keys are encrypted locally by the user's password before being sent off to the server during the account signup process.
+- Each (encrypted) secret belongs to a project and environment.
+- Each project has an (encrypted) project key used to encrypt the secrets within that project; Infisical stores copies of the project key, for each member of that project, encrypted under each member's public key.
+- Secrets are encrypted symmetrically by your copy of the project key belonging to the project containing.
+- Infisical uses AES256-GCM and [TweetNaCl.js](https://tweetnacl.js.org/#/) for symmetric and asymmetric encryption/decryption operations.
+
+
+ Infisical's system ensures greater security such that secrets are
+ encrypted/decrypted on the client-side but requires users to properly
+ implement cryptographic operations to maintain end-to-end encryption (E2EE).
+ We're
+
diff --git a/docs/api-reference/overview/usage.mdx b/docs/api-reference/overview/usage.mdx
new file mode 100644
index 000000000..9f23080c7
--- /dev/null
+++ b/docs/api-reference/overview/usage.mdx
@@ -0,0 +1,18 @@
+---
+title: "Usage"
+---
+
+Prerequisites:
+
+- Set up and add envars to [Infisical Cloud](https://app.infisical.com) or your self-hosted instance.
+- Obtain an API Key in your user settings to be included in requests to the Infisical API.
+
+Using Infisical's API to manage secrets requires a basic understanding of the system and its underlying cryptography detailed [here](/security/overview).
+
+## Concepts
+
+- Each user has a public/private key pair that is stored with the platform; private keys are encrypted locally by the user's password before being sent off to the server during the account signup process.
+- Each (encrypted) secret belongs to a project and environment.
+- Each project has an (encrypted) project key used to encrypt the secrets within that project; Infisical stores copies of the project key, for each member of that project, encrypted under each member's public key.
+- Secrets are encrypted symmetrically by your copy of the project key belonging to the project containing.
+- Infisical uses AES256-GCM and [TweetNaCl.js](https://tweetnacl.js.org/#/) for symmetric and asymmetric encryption/decryption operations.
diff --git a/docs/mint.json b/docs/mint.json
index 4b83b5c17..77d17c6c3 100644
--- a/docs/mint.json
+++ b/docs/mint.json
@@ -21,6 +21,16 @@
"to": "#F8B7BD"
}
},
+ "api": {
+ "baseUrl": [
+ "https://app.infisical.com",
+ "http://localhost:8080"
+ ],
+ "auth": {
+ "method": "api-key",
+ "name": "X-API-KEY"
+ }
+ },
"topbarLinks": [
{ "name": "Log In", "url": "https://app.infisical.com/login" }
],
@@ -39,6 +49,11 @@
"icon": "server",
"url": "self-hosting"
},
+ {
+ "name": "API Reference",
+ "icon": "cloud",
+ "url": "api-reference"
+ },
{
"name": "Integrations",
"icon": "plug",
@@ -125,6 +140,56 @@
"self-hosting/configuration/email"
]
},
+ {
+ "group": "Overview",
+ "pages": [
+ "api-reference/overview/introduction",
+ "api-reference/overview/authentication",
+ {
+ "group": "Examples",
+ "pages": [
+ "api-reference/overview/examples/create-secrets",
+ "api-reference/overview/examples/retrieve-secrets",
+ "api-reference/overview/examples/update-secrets",
+ "api-reference/overview/examples/delete-secrets"
+ ]
+ }
+ ]
+ },
+ {
+ "group": "Endpoints",
+ "pages": [
+ {
+ "group": "Users",
+ "pages": [
+ "api-reference/endpoints/users/me"
+ ]
+ },
+ {
+ "group": "Projects",
+ "pages": [
+ "api-reference/endpoints/workspaces/memberships",
+ "api-reference/endpoints/workspaces/update-membership",
+ "api-reference/endpoints/workspaces/delete-membership",
+ "api-reference/endpoints/workspaces/workspace-key",
+ "api-reference/endpoints/workspaces/logs",
+ "api-reference/endpoints/workspaces/secret-snapshots",
+ "api-reference/endpoints/workspaces/rollback-snapshot"
+ ]
+ },
+ {
+ "group": "Secrets",
+ "pages": [
+ "api-reference/endpoints/secrets/create",
+ "api-reference/endpoints/secrets/read",
+ "api-reference/endpoints/secrets/update",
+ "api-reference/endpoints/secrets/delete",
+ "api-reference/endpoints/secrets/versions",
+ "api-reference/endpoints/secrets/rollback-version"
+ ]
+ }
+ ]
+ },
{
"group": "Integrations",
"pages": ["integrations/overview"]
diff --git a/docs/spec.yaml b/docs/spec.yaml
new file mode 100644
index 000000000..991682871
--- /dev/null
+++ b/docs/spec.yaml
@@ -0,0 +1,2327 @@
+openapi: 3.0.0
+info:
+ title: Infisical API
+ description: List of all available APIs that can be consumed
+ version: 1.0.0
+servers:
+ - url: https://infisical.com
+ description: Production server
+ - url: http://localhost:8080
+ description: Local server
+paths:
+ /api/v1/secret/{secretId}/secret-versions:
+ get:
+ summary: Return secret versions
+ description: Return secret versions
+ parameters:
+ - name: secretId
+ in: path
+ required: true
+ schema:
+ type: string
+ description: ID of secret
+ - name: offset
+ description: Number of versions to skip
+ required: false
+ in: query
+ schema:
+ type: string
+ - name: limit
+ description: Maximum number of versions to return
+ required: false
+ in: query
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ secretVersions:
+ type: array
+ items:
+ $ref: '#/components/schemas/SecretVersion'
+ description: Secret versions
+ '400':
+ description: Bad Request
+ security:
+ - apiKeyAuth: []
+ /api/v1/secret/{secretId}/secret-versions/rollback:
+ post:
+ summary: Roll back secret to a version.
+ description: Roll back secret to a version.
+ parameters:
+ - name: secretId
+ in: path
+ required: true
+ schema:
+ type: string
+ description: ID of secret
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ secret:
+ type: object
+ $ref: '#/components/schemas/Secret'
+ description: Secret rolled back to
+ '400':
+ description: Bad Request
+ security:
+ - apiKeyAuth: []
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ version:
+ type: integer
+ description: Version of secret to roll back to
+ /api/v1/secret-snapshot/{secretSnapshotId}:
+ get:
+ description: ''
+ parameters:
+ - name: secretSnapshotId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v1/workspace/{workspaceId}/secret-snapshots:
+ get:
+ summary: Return project secret snapshot ids
+ description: Return project secret snapshots ids
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ description: ID of project
+ - name: offset
+ description: Number of secret snapshots to skip
+ required: false
+ in: query
+ schema:
+ type: string
+ - name: limit
+ description: Maximum number of secret snapshots to return
+ required: false
+ in: query
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ secretSnapshots:
+ type: array
+ items:
+ $ref: '#/components/schemas/SecretSnapshot'
+ description: Project secret snapshots
+ '400':
+ description: Bad Request
+ security:
+ - apiKeyAuth: []
+ /api/v1/workspace/{workspaceId}/secret-snapshots/count:
+ get:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v1/workspace/{workspaceId}/secret-snapshots/rollback:
+ post:
+ summary: >-
+ Roll back project secrets to those captured in a secret snapshot
+ version.
+ description: >-
+ Roll back project secrets to those captured in a secret snapshot
+ version.
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ description: ID of project
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ secrets:
+ type: array
+ items:
+ $ref: '#/components/schemas/Secret'
+ description: Secrets rolled back to
+ '400':
+ description: Bad Request
+ security:
+ - apiKeyAuth: []
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ version:
+ type: integer
+ description: Version of secret snapshot to roll back to
+ /api/v1/workspace/{workspaceId}/logs:
+ get:
+ summary: Return project (audit) logs
+ description: Return project (audit) logs
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ description: ID of project
+ - name: userId
+ description: ID of project member
+ required: false
+ in: query
+ schema:
+ type: string
+ - name: offset
+ description: Number of logs to skip
+ required: false
+ in: query
+ schema:
+ type: string
+ - name: limit
+ description: Maximum number of logs to return
+ required: false
+ in: query
+ schema:
+ type: string
+ - name: sortBy
+ description: Order to sort the logs by
+ schema:
+ type: string
+ enum:
+ - oldest
+ - recent
+ required: false
+ in: query
+ - name: actionNames
+ description: Names of log actions (comma-separated)
+ required: false
+ in: query
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ logs:
+ type: array
+ items:
+ $ref: '#/components/schemas/Log'
+ description: Project logs
+ '400':
+ description: Bad Request
+ security:
+ - apiKeyAuth: []
+ /api/v1/action/{actionId}:
+ get:
+ description: ''
+ parameters:
+ - name: actionId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ /api/v1/signup/email/signup:
+ post:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ '403':
+ description: Forbidden
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ email:
+ example: any
+ /api/v1/signup/email/verify:
+ post:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ '403':
+ description: Forbidden
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ email:
+ example: any
+ code:
+ example: any
+ /api/v1/signup/complete-account/signup:
+ post:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ '403':
+ description: Forbidden
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ email:
+ example: any
+ firstName:
+ example: any
+ lastName:
+ example: any
+ publicKey:
+ example: any
+ encryptedPrivateKey:
+ example: any
+ iv:
+ example: any
+ tag:
+ example: any
+ salt:
+ example: any
+ verifier:
+ example: any
+ organizationName:
+ example: any
+ /api/v1/signup/complete-account/invite:
+ post:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ '403':
+ description: Forbidden
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ email:
+ example: any
+ firstName:
+ example: any
+ lastName:
+ example: any
+ publicKey:
+ example: any
+ encryptedPrivateKey:
+ example: any
+ iv:
+ example: any
+ tag:
+ example: any
+ salt:
+ example: any
+ verifier:
+ example: any
+ /api/v1/auth/token:
+ post:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v1/auth/login1:
+ post:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ email:
+ example: any
+ clientPublicKey:
+ example: any
+ /api/v1/auth/login2:
+ post:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ email:
+ example: any
+ clientProof:
+ example: any
+ /api/v1/auth/logout:
+ post:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v1/auth/checkAuth:
+ post:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ /api/v1/bot/{workspaceId}:
+ get:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v1/bot/{botId}/active:
+ patch:
+ description: ''
+ parameters:
+ - name: botId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ isActive:
+ example: any
+ botKey:
+ example: any
+ /api/v1/user/:
+ get:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ /api/v1/user-action/:
+ post:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ action:
+ example: any
+ get:
+ description: ''
+ parameters:
+ - name: action
+ in: query
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v1/organization/:
+ get:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ post:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ organizationName:
+ example: any
+ /api/v1/organization/{organizationId}:
+ get:
+ description: ''
+ parameters:
+ - name: organizationId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v1/organization/{organizationId}/users:
+ get:
+ description: ''
+ parameters:
+ - name: organizationId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v1/organization/{organizationId}/my-workspaces:
+ get:
+ description: ''
+ parameters:
+ - name: organizationId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v1/organization/{organizationId}/name:
+ patch:
+ description: ''
+ parameters:
+ - name: organizationId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ name:
+ example: any
+ /api/v1/organization/{organizationId}/incidentContactOrg:
+ get:
+ description: ''
+ parameters:
+ - name: organizationId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ post:
+ description: ''
+ parameters:
+ - name: organizationId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ email:
+ example: any
+ delete:
+ description: ''
+ parameters:
+ - name: organizationId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ email:
+ example: any
+ /api/v1/organization/{organizationId}/customer-portal-session:
+ post:
+ description: ''
+ parameters:
+ - name: organizationId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v1/organization/{organizationId}/subscriptions:
+ get:
+ description: ''
+ parameters:
+ - name: organizationId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v1/workspace/{workspaceId}/keys:
+ get:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v1/workspace/{workspaceId}/users:
+ get:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v1/workspace/:
+ get:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ post:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ workspaceName:
+ example: any
+ organizationId:
+ example: any
+ /api/v1/workspace/{workspaceId}:
+ get:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ delete:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v1/workspace/{workspaceId}/name:
+ post:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ name:
+ example: any
+ /api/v1/workspace/{workspaceId}/invite-signup:
+ post:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ email:
+ example: any
+ /api/v1/workspace/{workspaceId}/integrations:
+ get:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v1/workspace/{workspaceId}/authorizations:
+ get:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v1/workspace/{workspaceId}/service-tokens:
+ get:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v1/membership-org/membershipOrg/{membershipOrgId}/change-role:
+ post:
+ description: ''
+ parameters:
+ - name: membershipOrgId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ /api/v1/membership-org/{membershipOrgId}:
+ delete:
+ description: ''
+ parameters:
+ - name: membershipOrgId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '400':
+ description: Bad Request
+ /api/v1/membership/{workspaceId}/connect:
+ get:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v1/membership/{membershipId}:
+ delete:
+ description: ''
+ parameters:
+ - name: membershipId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v1/membership/{membershipId}/change-role:
+ post:
+ description: ''
+ parameters:
+ - name: membershipId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ role:
+ example: any
+ /api/v1/key/{workspaceId}:
+ post:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ key:
+ example: any
+ /api/v1/key/{workspaceId}/latest:
+ get:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v1/invite-org/signup:
+ post:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ organizationId:
+ example: any
+ inviteeEmail:
+ example: any
+ /api/v1/invite-org/verify:
+ post:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ email:
+ example: any
+ code:
+ example: any
+ /api/v1/secret/{workspaceId}:
+ post:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ secrets:
+ example: any
+ keys:
+ example: any
+ environment:
+ example: any
+ channel:
+ example: any
+ get:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ - name: environment
+ in: query
+ schema:
+ type: string
+ - name: channel
+ in: query
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v1/secret/{workspaceId}/service-token:
+ get:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ - name: environment
+ in: query
+ schema:
+ type: string
+ - name: channel
+ in: query
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v1/service-token/:
+ get:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ post:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ name:
+ example: any
+ workspaceId:
+ example: any
+ environment:
+ example: any
+ expiresIn:
+ example: any
+ publicKey:
+ example: any
+ encryptedKey:
+ example: any
+ nonce:
+ example: any
+ /api/v1/password/srp1:
+ post:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ clientPublicKey:
+ example: any
+ /api/v1/password/change-password:
+ post:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ clientProof:
+ example: any
+ encryptedPrivateKey:
+ example: any
+ iv:
+ example: any
+ tag:
+ example: any
+ salt:
+ example: any
+ verifier:
+ example: any
+ /api/v1/password/email/password-reset:
+ post:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ '403':
+ description: Forbidden
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ email:
+ example: any
+ /api/v1/password/email/password-reset-verify:
+ post:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ '403':
+ description: Forbidden
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ email:
+ example: any
+ code:
+ example: any
+ /api/v1/password/backup-private-key:
+ get:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ post:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ clientProof:
+ example: any
+ encryptedPrivateKey:
+ example: any
+ iv:
+ example: any
+ tag:
+ example: any
+ salt:
+ example: any
+ verifier:
+ example: any
+ /api/v1/password/password-reset:
+ post:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ encryptedPrivateKey:
+ example: any
+ iv:
+ example: any
+ tag:
+ example: any
+ salt:
+ example: any
+ verifier:
+ example: any
+ /api/v1/stripe/webhook:
+ post:
+ description: ''
+ parameters:
+ - name: stripe-signature
+ in: header
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v1/integration/{integrationId}:
+ patch:
+ description: ''
+ parameters:
+ - name: integrationId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ app:
+ example: any
+ environment:
+ example: any
+ isActive:
+ example: any
+ target:
+ example: any
+ context:
+ example: any
+ siteId:
+ example: any
+ delete:
+ description: ''
+ parameters:
+ - name: integrationId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v1/integration-auth/integration-options:
+ get:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ /api/v1/integration-auth/oauth-token:
+ post:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ workspaceId:
+ example: any
+ code:
+ example: any
+ integration:
+ example: any
+ /api/v1/integration-auth/{integrationAuthId}/apps:
+ get:
+ description: ''
+ parameters:
+ - name: integrationAuthId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v1/integration-auth/{integrationAuthId}:
+ delete:
+ description: ''
+ parameters:
+ - name: integrationAuthId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v2/users/me:
+ get:
+ summary: Retrieve the current user on the request
+ description: Retrieve the current user on the request
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ user:
+ type: object
+ $ref: '#/components/schemas/CurrentUser'
+ description: Current user on request
+ '400':
+ description: Bad Request
+ security:
+ - apiKeyAuth: []
+ /api/v2/workspace/{workspaceId}/secrets:
+ post:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ secrets:
+ example: any
+ keys:
+ example: any
+ environment:
+ example: any
+ channel:
+ example: any
+ get:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ - name: environment
+ in: query
+ schema:
+ type: string
+ - name: channel
+ in: query
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v2/workspace/{workspaceId}/encrypted-key:
+ get:
+ summary: Return encrypted project key
+ description: Return encrypted project key
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ description: ID of project
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/ProjectKey'
+ description: Encrypted project key for the given project
+ '400':
+ description: Bad Request
+ security:
+ - apiKeyAuth: []
+ /api/v2/workspace/{workspaceId}/service-token-data:
+ get:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v2/workspace/{workspaceId}/memberships:
+ get:
+ summary: Return project memberships
+ description: Return project memberships
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ description: ID of project
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ memberships:
+ type: array
+ items:
+ $ref: '#/components/schemas/Membership'
+ description: Memberships of project
+ '400':
+ description: Bad Request
+ security:
+ - apiKeyAuth: []
+ /api/v2/workspace/{workspaceId}/memberships/{membershipId}:
+ delete:
+ summary: Delete project membership
+ description: Delete project membership
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ description: ID of project
+ - name: membershipId
+ in: path
+ required: true
+ schema:
+ type: string
+ description: ID of membership
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ membership:
+ $ref: '#/components/schemas/Membership'
+ description: Deleted membership
+ '400':
+ description: Bad Request
+ security:
+ - apiKeyAuth: []
+ patch:
+ summary: Update project membership
+ description: Update project membership
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ description: ID of project
+ - name: membershipId
+ in: path
+ required: true
+ schema:
+ type: string
+ description: ID of membership
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ membership:
+ $ref: '#/components/schemas/Membership'
+ description: Updated membership
+ '400':
+ description: Bad Request
+ security:
+ - apiKeyAuth: []
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ role:
+ type: string
+ description: Role of membership - either admin or member
+ /api/v2/secret/batch-create/workspace/{workspaceId}/environment/{environment}:
+ post:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ - name: environment
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ secrets:
+ example: any
+ /api/v2/secret/workspace/{workspaceId}/environment/{environment}:
+ post:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ - name: environment
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ secret:
+ example: any
+ /api/v2/secret/workspace/{workspaceId}:
+ get:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ - name: environment
+ in: query
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ /api/v2/secret/{secretId}:
+ get:
+ description: ''
+ parameters:
+ - name: secretId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ delete:
+ description: ''
+ parameters:
+ - name: secretId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ /api/v2/secret/batch/workspace/{workspaceId}/environment/{environmentName}:
+ delete:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ - name: environmentName
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ secretIds:
+ example: any
+ /api/v2/secret/batch-modify/workspace/{workspaceId}/environment/{environmentName}:
+ patch:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ - name: environmentName
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ secrets:
+ example: any
+ /api/v2/secret/workspace/{workspaceId}/environment/{environmentName}:
+ patch:
+ description: ''
+ parameters:
+ - name: workspaceId
+ in: path
+ required: true
+ schema:
+ type: string
+ - name: environmentName
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ secret:
+ example: any
+ /api/v2/secrets/:
+ post:
+ summary: Create new secret(s)
+ description: Create one or many secrets for a given project and environment.
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ secrets:
+ type: array
+ items:
+ $ref: '#/components/schemas/Secret'
+ description: >-
+ Newly-created secrets for the given project and
+ environment
+ security:
+ - apiKeyAuth: []
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ workspaceId:
+ type: string
+ description: ID of project
+ environment:
+ type: string
+ description: Environment within project
+ secrets:
+ $ref: '#/components/schemas/CreateSecret'
+ description: Secret(s) to create - object or array of objects
+ get:
+ summary: Read secrets
+ description: Read secrets from a project and environment
+ parameters:
+ - name: workspaceId
+ description: ID of project
+ required: true
+ in: query
+ schema:
+ type: string
+ - name: environment
+ description: Environment within project
+ required: true
+ in: query
+ schema:
+ type: string
+ - name: content
+ in: query
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ secrets:
+ type: array
+ items:
+ $ref: '#/components/schemas/Secret'
+ description: Secrets for the given project and environment
+ security:
+ - apiKeyAuth: []
+ patch:
+ summary: Update secret(s)
+ description: Update secret(s)
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ secrets:
+ type: array
+ items:
+ $ref: '#/components/schemas/Secret'
+ description: Updated secrets
+ security:
+ - apiKeyAuth: []
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ secrets:
+ $ref: '#/components/schemas/UpdateSecret'
+ description: Secret(s) to update - object or array of objects
+ delete:
+ summary: Delete secret(s)
+ description: Delete one or many secrets by their ID(s)
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ secrets:
+ type: array
+ items:
+ $ref: '#/components/schemas/Secret'
+ description: Deleted secrets
+ security:
+ - apiKeyAuth: []
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ secretIds:
+ type: string
+ description: ID(s) of secrets - string or array of strings
+ /api/v2/service-token/:
+ get:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ post:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ name:
+ example: any
+ workspaceId:
+ example: any
+ environment:
+ example: any
+ encryptedKey:
+ example: any
+ iv:
+ example: any
+ tag:
+ example: any
+ expiresIn:
+ example: any
+ /api/v2/service-token/{serviceTokenDataId}:
+ delete:
+ description: ''
+ parameters:
+ - name: serviceTokenDataId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/v2/api-key-data/:
+ get:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ post:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ name:
+ example: any
+ expiresIn:
+ example: any
+ /api/v2/api-key-data/{apiKeyDataId}:
+ delete:
+ description: ''
+ parameters:
+ - name: apiKeyDataId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ '400':
+ description: Bad Request
+ /api/status:
+ get:
+ description: ''
+ parameters: []
+ responses:
+ '200':
+ description: OK
+components:
+ schemas:
+ CurrentUser:
+ type: object
+ properties:
+ _id:
+ type: string
+ example: ''
+ email:
+ type: string
+ example: ''
+ firstName:
+ type: string
+ example: ''
+ lastName:
+ type: string
+ example: ''
+ publicKey:
+ type: string
+ example: ''
+ encryptedPrivateKey:
+ type: string
+ example: ''
+ iv:
+ type: string
+ example: ''
+ tag:
+ type: string
+ example: ''
+ updatedAt:
+ type: string
+ example: ''
+ createdAt:
+ type: string
+ example: ''
+ Membership:
+ type: object
+ properties:
+ user:
+ type: object
+ properties:
+ _id:
+ type: string
+ example: ''
+ email:
+ type: string
+ example: ''
+ firstName:
+ type: string
+ example: ''
+ lastName:
+ type: string
+ example: ''
+ publicKey:
+ type: string
+ example: ''
+ updatedAt:
+ type: string
+ example: ''
+ createdAt:
+ type: string
+ example: ''
+ workspace:
+ type: string
+ example: ''
+ role:
+ type: string
+ example: admin
+ ProjectKey:
+ type: object
+ properties:
+ encryptedkey:
+ type: string
+ example: ''
+ nonce:
+ type: string
+ example: ''
+ sender:
+ type: object
+ properties:
+ publicKey:
+ type: string
+ example: ''
+ receiver:
+ type: string
+ example: ''
+ workspace:
+ type: string
+ example: ''
+ CreateSecret:
+ type: object
+ properties:
+ type:
+ type: string
+ example: shared
+ secretKeyCiphertext:
+ type: string
+ example: ''
+ secretKeyIV:
+ type: string
+ example: ''
+ secretKeyTag:
+ type: string
+ example: ''
+ secretValueCiphertext:
+ type: string
+ example: ''
+ secretValueIV:
+ type: string
+ example: ''
+ secretValueTag:
+ type: string
+ example: ''
+ secretCommentCiphertext:
+ type: string
+ example: ''
+ secretCommentIV:
+ type: string
+ example: ''
+ secretCommentTag:
+ type: string
+ example: ''
+ UpdateSecret:
+ type: object
+ properties:
+ id:
+ type: string
+ example: ''
+ secretKeyCiphertext:
+ type: string
+ example: ''
+ secretKeyIV:
+ type: string
+ example: ''
+ secretKeyTag:
+ type: string
+ example: ''
+ secretValueCiphertext:
+ type: string
+ example: ''
+ secretValueIV:
+ type: string
+ example: ''
+ secretValueTag:
+ type: string
+ example: ''
+ secretCommentCiphertext:
+ type: string
+ example: ''
+ secretCommentIV:
+ type: string
+ example: ''
+ secretCommentTag:
+ type: string
+ example: ''
+ Secret:
+ type: object
+ properties:
+ _id:
+ type: string
+ example: ''
+ version:
+ type: number
+ example: 1
+ workspace:
+ type: string
+ example: ''
+ type:
+ type: string
+ example: shared
+ user: {}
+ secretKeyCiphertext:
+ type: string
+ example: ''
+ secretKeyIV:
+ type: string
+ example: ''
+ secretKeyTag:
+ type: string
+ example: ''
+ secretValueCiphertext:
+ type: string
+ example: ''
+ secretValueIV:
+ type: string
+ example: ''
+ secretValueTag:
+ type: string
+ example: ''
+ secretCommentCiphertext:
+ type: string
+ example: ''
+ secretCommentIV:
+ type: string
+ example: ''
+ secretCommentTag:
+ type: string
+ example: ''
+ updatedAt:
+ type: string
+ example: ''
+ createdAt:
+ type: string
+ example: ''
+ Log:
+ type: object
+ properties:
+ _id:
+ type: string
+ example: ''
+ user:
+ type: object
+ properties:
+ _id:
+ type: string
+ example: ''
+ email:
+ type: string
+ example: ''
+ firstName:
+ type: string
+ example: ''
+ lastName:
+ type: string
+ example: ''
+ workspace:
+ type: string
+ example: ''
+ actionNames:
+ type: array
+ example:
+ - addSecrets
+ items:
+ type: string
+ actions:
+ type: array
+ items:
+ type: object
+ properties:
+ name:
+ type: string
+ example: addSecrets
+ user:
+ type: string
+ example: ''
+ workspace:
+ type: string
+ example: ''
+ payload:
+ type: array
+ items:
+ type: object
+ properties:
+ oldSecretVersion:
+ type: string
+ example: ''
+ newSecretVersion:
+ type: string
+ example: ''
+ channel:
+ type: string
+ example: cli
+ ipAddress:
+ type: string
+ example: 192.168.0.1
+ updatedAt:
+ type: string
+ example: ''
+ createdAt:
+ type: string
+ example: ''
+ SecretSnapshot:
+ type: object
+ properties:
+ workspace:
+ type: string
+ example: ''
+ version:
+ type: number
+ example: 1
+ secretVersions:
+ type: array
+ items:
+ type: object
+ properties:
+ _id:
+ type: string
+ example: ''
+ SecretVersion:
+ type: object
+ properties:
+ _id:
+ type: string
+ example: ''
+ secret:
+ type: string
+ example: ''
+ version:
+ type: number
+ example: 1
+ workspace:
+ type: string
+ example: ''
+ type:
+ type: string
+ example: ''
+ user:
+ type: string
+ example: ''
+ environment:
+ type: string
+ example: ''
+ isDeleted:
+ type: string
+ example: ''
+ secretKeyCiphertext:
+ type: string
+ example: ''
+ secretKeyIV:
+ type: string
+ example: ''
+ secretKeyTag:
+ type: string
+ example: ''
+ secretValueCiphertext:
+ type: string
+ example: ''
+ secretValueIV:
+ type: string
+ example: ''
+ secretValueTag:
+ type: string
+ example: ''
+ securitySchemes:
+ bearerAuth:
+ type: http
+ scheme: bearer
+ bearerFormat: JWT
+ description: >-
+ This security definition uses the HTTP 'bearer' scheme, which allows the
+ client to authenticate using a JSON Web Token (JWT) that is passed in
+ the Authorization header of the request.
+ apiKeyAuth:
+ type: apiKey
+ in: header
+ name: X-API-Key
+ description: >-
+ This security definition uses an API key, which is passed in the header
+ of the request as the value of the "X-API-Key" header. The client must
+ provide a valid key in order to access the API.
diff --git a/frontend/components/utilities/attemptLogin.ts b/frontend/components/utilities/attemptLogin.ts
index f1aa03ffb..1a960af10 100644
--- a/frontend/components/utilities/attemptLogin.ts
+++ b/frontend/components/utilities/attemptLogin.ts
@@ -61,7 +61,7 @@ const attemptLogin = async (
// if everything works, go the main dashboard page.
const { token, publicKey, encryptedPrivateKey, iv, tag } =
await login2(email, clientProof);
-
+
SecurityClient.setToken(token);
const privateKey = Aes256Gcm.decrypt({