diff --git a/backend/spec.json b/backend/spec.json
index baad21e7e..e28c66eaa 100644
--- a/backend/spec.json
+++ b/backend/spec.json
@@ -18,7 +18,8 @@
"paths": {
"/api/v1/secret/{secretId}/secret-versions": {
"get": {
- "description": "",
+ "summary": "Return secret versions",
+ "description": "Return secret versions",
"parameters": [
{
"name": "secretId",
@@ -26,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"
@@ -37,6 +41,8 @@
},
{
"name": "limit",
+ "description": "Maximum number of versions to return",
+ "required": false,
"in": "query",
"schema": {
"type": "string"
@@ -45,17 +51,39 @@
],
"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": {
- "description": "",
+ "summary": "Roll back secret to a version.",
+ "description": "Roll back secret to a version.",
"parameters": [
{
"name": "secretId",
@@ -63,25 +91,47 @@
"required": true,
"schema": {
"type": "string"
- }
+ },
+ "description": "ID of secret"
}
],
"responses": {
"200": {
- "description": "OK"
+ "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": {
- "example": "any"
+ "type": "integer",
+ "description": "Version of secret to roll back to"
}
}
}
@@ -115,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",
@@ -123,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"
@@ -134,6 +188,8 @@
},
{
"name": "limit",
+ "description": "Maximum number of secret snapshots to return",
+ "required": false,
"in": "query",
"schema": {
"type": "string"
@@ -142,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": {
@@ -175,8 +252,8 @@
},
"/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",
+ "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",
@@ -194,11 +271,16 @@
"content": {
"application/json": {
"schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/Secret"
- },
- "description": "Array of secrets captured in the secret snapshot"
+ "type": "object",
+ "properties": {
+ "secrets": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Secret"
+ },
+ "description": "Secrets rolled back to"
+ }
+ }
}
}
}
@@ -232,18 +314,31 @@
},
"/api/v1/workspace/{workspaceId}/logs": {
"get": {
- "description": "",
+ "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"
@@ -251,6 +346,8 @@
},
{
"name": "limit",
+ "description": "Maximum number of logs to return",
+ "required": false,
"in": "query",
"schema": {
"type": "string"
@@ -258,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"
@@ -280,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}": {
@@ -2117,8 +2236,13 @@
"application/json": {
"schema": {
"type": "object",
- "$ref": "#/components/schemas/CurrentUser",
- "description": "Current user on request"
+ "properties": {
+ "user": {
+ "type": "object",
+ "$ref": "#/components/schemas/CurrentUser",
+ "description": "Current user on request"
+ }
+ }
}
}
}
@@ -2279,6 +2403,170 @@
}
}
},
+ "/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": "",
@@ -2565,11 +2853,16 @@
"content": {
"application/json": {
"schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/Secret"
- },
- "description": "Array of newly-created secrets for the given project and environment"
+ "type": "object",
+ "properties": {
+ "secrets": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Secret"
+ },
+ "description": "Newly-created secrets for the given project and environment"
+ }
+ }
}
}
}
@@ -2608,18 +2901,49 @@
"get": {
"summary": "Read secrets",
"description": "Read secrets from a project and environment",
- "parameters": [],
+ "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": "array",
- "items": {
- "$ref": "#/components/schemas/Secret"
- },
- "description": "Array of secrets for the given project and environment"
+ "type": "object",
+ "properties": {
+ "secrets": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Secret"
+ },
+ "description": "Secrets for the given project and environment"
+ }
+ }
}
}
}
@@ -2629,27 +2953,7 @@
{
"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"
- }
- }
- }
- }
- }
- }
+ ]
},
"patch": {
"summary": "Update secret(s)",
@@ -2661,11 +2965,16 @@
"content": {
"application/json": {
"schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/Secret"
- },
- "description": "Array of newly-updated secrets for the given project and environment"
+ "type": "object",
+ "properties": {
+ "secrets": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Secret"
+ },
+ "description": "Updated secrets"
+ }
+ }
}
}
}
@@ -2703,11 +3012,16 @@
"content": {
"application/json": {
"schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/Secret"
- },
- "description": "Array of deleted secrets"
+ "type": "object",
+ "properties": {
+ "secrets": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Secret"
+ },
+ "description": "Deleted secrets"
+ }
+ }
}
}
}
@@ -2942,6 +3256,52 @@
}
}
},
+ "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": {
@@ -3127,6 +3487,187 @@
"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": {
diff --git a/backend/src/controllers/v2/secretsController.ts b/backend/src/controllers/v2/secretsController.ts
index 31ad0d94c..43434df63 100644
--- a/backend/src/controllers/v2/secretsController.ts
+++ b/backend/src/controllers/v2/secretsController.ts
@@ -58,11 +58,16 @@ export const createSecrets = async (req: Request, res: Response) => {
content: {
"application/json": {
"schema": {
- "type": "array",
- "items": {
- $ref: "#/components/schemas/Secret"
- },
- "description": "Array of newly-created secrets for the given project and environment"
+ "type": "object",
+ "properties": {
+ "secrets": {
+ "type": "array",
+ "items": {
+ $ref: "#/components/schemas/Secret"
+ },
+ "description": "Newly-created secrets for the given project and environment"
+ }
+ }
}
}
}
@@ -205,36 +210,32 @@ export const getSecrets = async (req: Request, res: Response) => {
"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"
- }
- }
- }
- }
- }
- }
+ #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": "array",
- "items": {
- $ref: "#/components/schemas/Secret"
- },
- "description": "Array of secrets for the given project and environment"
+ "type": "object",
+ "properties": {
+ "secrets": {
+ "type": "array",
+ "items": {
+ $ref: "#/components/schemas/Secret"
+ },
+ "description": "Secrets for the given project and environment"
+ }
+ }
}
}
}
@@ -307,9 +308,6 @@ export const getSecrets = async (req: Request, res: Response) => {
* @param res
*/
export const updateSecrets = async (req: Request, res: Response) => {
-
- // TODO: fix update secret schema
-
/*
#swagger.summary = 'Update secret(s)'
#swagger.description = 'Update secret(s)'
@@ -339,15 +337,20 @@ export const updateSecrets = async (req: Request, res: Response) => {
content: {
"application/json": {
"schema": {
- "type": "array",
- "items": {
- $ref: "#/components/schemas/Secret"
- },
- "description": "Array of newly-updated secrets for the given project and environment"
+ "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';
@@ -544,12 +547,17 @@ export const deleteSecrets = async (req: Request, res: Response) => {
#swagger.responses[200] = {
content: {
"application/json": {
- schema: {
- "type": "array",
- "items": {
- $ref: "#/components/schemas/Secret"
- },
- "description": "Array of deleted secrets"
+ "schema": {
+ "type": "object",
+ "properties": {
+ "secrets": {
+ "type": "array",
+ "items": {
+ $ref: "#/components/schemas/Secret"
+ },
+ "description": "Deleted secrets"
+ }
+ }
}
}
}
diff --git a/backend/src/controllers/v2/usersController.ts b/backend/src/controllers/v2/usersController.ts
index 194e508b3..7ad247ff0 100644
--- a/backend/src/controllers/v2/usersController.ts
+++ b/backend/src/controllers/v2/usersController.ts
@@ -18,8 +18,13 @@ export const getMe = async (req: Request, res: Response) => {
"application/json": {
"schema": {
"type": "object",
- $ref: "#/components/schemas/CurrentUser",
- "description": "Current user on request"
+ "properties": {
+ "user": {
+ "type": "object",
+ $ref: "#/components/schemas/CurrentUser",
+ "description": "Current user on request"
+ }
+ }
}
}
}
diff --git a/backend/src/controllers/v2/workspaceController.ts b/backend/src/controllers/v2/workspaceController.ts
index 6acec644d..2b3aeda1d 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,
@@ -242,4 +244,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 members'
+ });
+ }
+
+ 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 f29c42861..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;
@@ -79,8 +124,8 @@ export const getWorkspaceSecretSnapshotsCount = async (req: Request, res: Respon
*/
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.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": []
@@ -113,11 +158,16 @@ export const rollbackWorkspaceSecretSnapshot = async (req: Request, res: Respons
content: {
"application/json": {
schema: {
- "type": "array",
- "items": {
- $ref: "#/components/schemas/Secret"
- },
- "description": "Array of secrets captured in the secret snapshot"
+ "type": "object",
+ "properties": {
+ "secrets": {
+ "type": "array",
+ "items": {
+ $ref: "#/components/schemas/Secret"
+ },
+ "description": "Secrets rolled back to"
+ }
+ }
}
}
}
@@ -276,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..b44d25ceb
--- /dev/null
+++ b/backend/src/middleware/requireMembershipAuth.ts
@@ -0,0 +1,57 @@
+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;
+
+ } 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/workspace.ts b/backend/src/routes/v2/workspace.ts
index c90834d6d..ebbfe0d4c 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
+
+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/index.ts b/backend/swagger/index.ts
index 91f27da44..cbf05ed7e 100644
--- a/backend/swagger/index.ts
+++ b/backend/swagger/index.ts
@@ -53,6 +53,19 @@ const generateOpenAPISpec = async () => {
updatedAt: '',
createdAt: ''
},
+ Membership: {
+ user: {
+ _id: '',
+ email: '',
+ firstName: '',
+ lastName: '',
+ publicKey: '',
+ updatedAt: '',
+ createdAt: ''
+ },
+ workspace: '',
+ role: 'admin'
+ },
ProjectKey: {
encryptedkey: '',
nonce: '',
@@ -103,6 +116,61 @@ const generateOpenAPISpec = async () => {
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: '',
}
}
};
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/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
index 2658676a3..05313eb8f 100644
--- a/docs/api-reference/endpoints/workspaces/workspace-key.mdx
+++ b/docs/api-reference/endpoints/workspaces/workspace-key.mdx
@@ -1,4 +1,4 @@
---
-title: "Get Project Key"
+title: "Get Key"
openapi: "GET /api/v2/workspace/{workspaceId}/encrypted-key"
---
diff --git a/docs/api-reference/overview/examples/create-secrets.mdx b/docs/api-reference/overview/examples/create-secrets.mdx
index 94f830094..b2afe1467 100644
--- a/docs/api-reference/overview/examples/create-secrets.mdx
+++ b/docs/api-reference/overview/examples/create-secrets.mdx
@@ -13,52 +13,140 @@ Prerequisites:
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 secrets with the project key.
-6. Send (encrypted) secrets to the Infical API
+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 axios = require("axios");
-const aes = require("aes-256-gcm");
-const nacl = require("tweetnacl");
-nacl.util = require("tweetnacl-util");
+const crypto = require('crypto');
+const axios = require('axios');
-const WORKSPACE_KEY = "3a7a243eb62078c13f09203e75e8cb32";
+const ALGORITHM = 'aes-256-gcm';
+const BLOCK_SIZE_BYTES = 16;
-const secretKey = "SOME_KEY";
-const secretValue = "SOME_VALUE";
+const encrypt = (
+ text,
+ secret
+) => {
+ const iv = crypto.randomBytes(BLOCK_SIZE_BYTES);
+ const cipher = crypto.createCipheriv(ALGORITHM, secret, iv);
-// encrypt key of secret
-const {
- ciphertext: secretKeyCiphertext,
- iv: secretKeyIV,
- tag: secretKeyTag,
-} = aes.encrypt(secretKey, WORKSPACE_KEY);
+ let ciphertext = cipher.update(text, 'utf8', 'base64');
+ ciphertext += cipher.final('base64');
+ return {
+ ciphertext,
+ iv: iv.toString('base64'),
+ tag: cipher.getAuthTag().toString('base64')
+ };
+}
-// encrypt value of secret
-const {
- ciphertext: secretValueCiphertext,
- iv: secretValueIV,
- tag: secretValueTag,
-} = aes.encrypt(secretKey, WORKSPACE_KEY);
+const decrypt = (ciphertext, iv, tag, secret) => {
+ const decipher = crypto.createDecipheriv(
+ ALGORITHM,
+ secret,
+ Buffer.from(iv, 'base64')
+ );
+ decipher.setAuthTag(Buffer.from(tag, 'base64'));
-// construct request body
-const secret = {
- secretKeyCiphertext,
- secretKeyIV,
- secretKeyTag,
- secretValueCiphertext,
- secretValueIV,
- secretValueTag,
-};
+ 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 in every major language.
-
+ 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
index 2a314e65d..2f34339c5 100644
--- a/docs/api-reference/overview/examples/retrieve-secrets.mdx
+++ b/docs/api-reference/overview/examples/retrieve-secrets.mdx
@@ -2,9 +2,141 @@
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 project key for the project.
-4. Decrypt the project key with your private key.
+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 secrets in your project.
+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
index b302710a3..b8566d9b3 100644
--- a/docs/api-reference/overview/examples/update-secrets.mdx
+++ b/docs/api-reference/overview/examples/update-secrets.mdx
@@ -2,9 +2,151 @@
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 secrets with the project key.
-6. Send (encrypted) updated secrets to the Infical API
+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 f93ba91fe..7babef955 100644
--- a/docs/api-reference/overview/introduction.mdx
+++ b/docs/api-reference/overview/introduction.mdx
@@ -2,8 +2,13 @@
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 HTTP requests. This can be useful for automating tasks, such as
+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.
@@ -17,3 +22,10 @@ Using Infisical's API to manage secrets requires a basic understanding of the sy
- 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/mint.json b/docs/mint.json
index 39a3726d9..77d17c6c3 100644
--- a/docs/mint.json
+++ b/docs/mint.json
@@ -150,7 +150,8 @@
"pages": [
"api-reference/overview/examples/create-secrets",
"api-reference/overview/examples/retrieve-secrets",
- "api-reference/overview/examples/update-secrets"
+ "api-reference/overview/examples/update-secrets",
+ "api-reference/overview/examples/delete-secrets"
]
}
]
@@ -167,7 +168,13 @@
{
"group": "Projects",
"pages": [
- "api-reference/endpoints/workspaces/workspace-key"
+ "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"
]
},
{
@@ -176,7 +183,9 @@
"api-reference/endpoints/secrets/create",
"api-reference/endpoints/secrets/read",
"api-reference/endpoints/secrets/update",
- "api-reference/endpoints/secrets/delete"
+ "api-reference/endpoints/secrets/delete",
+ "api-reference/endpoints/secrets/versions",
+ "api-reference/endpoints/secrets/rollback-version"
]
}
]
diff --git a/docs/spec.yaml b/docs/spec.yaml
index dd62f167b..991682871 100644
--- a/docs/spec.yaml
+++ b/docs/spec.yaml
@@ -11,48 +11,81 @@ servers:
paths:
/api/v1/secret/{secretId}/secret-versions:
get:
- description: ''
+ 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:
- description: ''
+ 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:
- example: any
+ type: integer
+ description: Version of secret to roll back to
/api/v1/secret-snapshot/{secretSnapshotId}:
get:
description: ''
@@ -69,26 +102,44 @@ paths:
description: Bad Request
/api/v1/workspace/{workspaceId}/secret-snapshots:
get:
- description: ''
+ 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: ''
@@ -105,60 +156,107 @@ paths:
description: Bad Request
/api/v1/workspace/{workspaceId}/secret-snapshots/rollback:
post:
- description: ''
+ 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:
- example: any
+ type: integer
+ description: Version of secret snapshot to roll back to
/api/v1/workspace/{workspaceId}/logs:
get:
- description: ''
+ 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
- in: query
+ description: Order to sort the logs by
schema:
type: string
- - name: userId
+ enum:
+ - oldest
+ - recent
+ required: false
in: query
- schema:
- type: string
- 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: ''
@@ -1285,8 +1383,11 @@ paths:
application/json:
schema:
type: object
- $ref: '#/components/schemas/CurrentUser'
- description: Current user on request
+ properties:
+ user:
+ type: object
+ $ref: '#/components/schemas/CurrentUser'
+ description: Current user on request
'400':
description: Bad Request
security:
@@ -1379,6 +1480,107 @@ paths:
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: ''
@@ -1554,12 +1756,15 @@ paths:
content:
application/json:
schema:
- type: array
- items:
- $ref: '#/components/schemas/Secret'
- description: >-
- Array of newly-created secrets for the given project and
- environment
+ type: object
+ properties:
+ secrets:
+ type: array
+ items:
+ $ref: '#/components/schemas/Secret'
+ description: >-
+ Newly-created secrets for the given project and
+ environment
security:
- apiKeyAuth: []
requestBody:
@@ -1581,32 +1786,38 @@ paths:
get:
summary: Read secrets
description: Read secrets from a project and environment
- parameters: []
+ 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: array
- items:
- $ref: '#/components/schemas/Secret'
- description: Array of secrets for the given project and environment
+ type: object
+ properties:
+ secrets:
+ type: array
+ items:
+ $ref: '#/components/schemas/Secret'
+ description: 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
patch:
summary: Update secret(s)
description: Update secret(s)
@@ -1617,12 +1828,13 @@ paths:
content:
application/json:
schema:
- type: array
- items:
- $ref: '#/components/schemas/Secret'
- description: >-
- Array of newly-updated secrets for the given project and
- environment
+ type: object
+ properties:
+ secrets:
+ type: array
+ items:
+ $ref: '#/components/schemas/Secret'
+ description: Updated secrets
security:
- apiKeyAuth: []
requestBody:
@@ -1645,10 +1857,13 @@ paths:
content:
application/json:
schema:
- type: array
- items:
- $ref: '#/components/schemas/Secret'
- description: Array of deleted secrets
+ type: object
+ properties:
+ secrets:
+ type: array
+ items:
+ $ref: '#/components/schemas/Secret'
+ description: Deleted secrets
security:
- apiKeyAuth: []
requestBody:
@@ -1783,12 +1998,51 @@ components:
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:
@@ -1925,6 +2179,135 @@ components:
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