mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-04 10:26:16 +00:00
Add eslint rule and fix as many issues as possible
This commit is contained in:
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"workbench.editor.wrapTabs": true
|
||||||
|
}
|
||||||
+11
-2
@@ -1,12 +1,21 @@
|
|||||||
{
|
{
|
||||||
"parser": "@typescript-eslint/parser",
|
"parser": "@typescript-eslint/parser",
|
||||||
"plugins": ["@typescript-eslint"],
|
"plugins": ["@typescript-eslint", "unused-imports"],
|
||||||
"extends": [
|
"extends": [
|
||||||
"eslint:recommended",
|
"eslint:recommended",
|
||||||
"plugin:@typescript-eslint/eslint-recommended",
|
"plugin:@typescript-eslint/eslint-recommended",
|
||||||
"plugin:@typescript-eslint/recommended"
|
"plugin:@typescript-eslint/recommended"
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-console": 2
|
"no-console": 2,
|
||||||
|
"quotes": ["error", "double", { "avoidEscape": true }],
|
||||||
|
"comma-dangle": ["error", "only-multiline"],
|
||||||
|
"@typescript-eslint/no-unused-vars": "off",
|
||||||
|
"unused-imports/no-unused-imports": "error",
|
||||||
|
"unused-imports/no-unused-vars": [
|
||||||
|
"warn",
|
||||||
|
{ "vars": "all", "varsIgnorePattern": "^_", "args": "after-used", "argsIgnorePattern": "^_" }
|
||||||
|
],
|
||||||
|
"sort-imports": ["error", { "ignoreDeclarationSort": true }]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"singleQuote": false,
|
||||||
|
"printWidth": 100,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"tabWidth": 2,
|
||||||
|
"semi": true
|
||||||
|
}
|
||||||
Vendored
+1
-1
@@ -14,7 +14,7 @@ declare global {
|
|||||||
JWT_SIGNUP_LIFETIME: string;
|
JWT_SIGNUP_LIFETIME: string;
|
||||||
JWT_SIGNUP_SECRET: string;
|
JWT_SIGNUP_SECRET: string;
|
||||||
MONGO_URL: string;
|
MONGO_URL: string;
|
||||||
NODE_ENV: 'development' | 'staging' | 'testing' | 'production';
|
NODE_ENV: "development" | "staging" | "testing" | "production";
|
||||||
VERBOSE_ERROR_OUTPUT: string;
|
VERBOSE_ERROR_OUTPUT: string;
|
||||||
LOKI_HOST: string;
|
LOKI_HOST: string;
|
||||||
CLIENT_ID_HEROKU: string;
|
CLIENT_ID_HEROKU: string;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
export default {
|
export default {
|
||||||
preset: 'ts-jest',
|
preset: "ts-jest",
|
||||||
testEnvironment: 'node',
|
testEnvironment: "node",
|
||||||
collectCoverageFrom: ['src/*.{js,ts}', '!**/node_modules/**'],
|
collectCoverageFrom: ["src/*.{js,ts}", "!**/node_modules/**"],
|
||||||
modulePaths: ['<rootDir>/src'],
|
modulePaths: ["<rootDir>/src"],
|
||||||
testMatch: ['<rootDir>/tests/**/*.test.ts'],
|
testMatch: ["<rootDir>/tests/**/*.test.ts"],
|
||||||
setupFiles: ['<rootDir>/test-resources/env-vars.js'],
|
setupFiles: ["<rootDir>/test-resources/env-vars.js"],
|
||||||
setupFilesAfterEnv: ['<rootDir>/tests/setupTests.ts']
|
setupFilesAfterEnv: ["<rootDir>/tests/setupTests.ts"],
|
||||||
};
|
};
|
||||||
|
|||||||
Generated
+42
-12479
File diff suppressed because it is too large
Load Diff
@@ -99,6 +99,7 @@
|
|||||||
"@typescript-eslint/parser": "^5.40.1",
|
"@typescript-eslint/parser": "^5.40.1",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^8.26.0",
|
"eslint": "^8.26.0",
|
||||||
|
"eslint-plugin-unused-imports": "^2.0.0",
|
||||||
"install": "^0.13.0",
|
"install": "^0.13.0",
|
||||||
"jest": "^29.3.1",
|
"jest": "^29.3.1",
|
||||||
"jest-junit": "^15.0.0",
|
"jest-junit": "^15.0.0",
|
||||||
|
|||||||
+66
-66
@@ -1,93 +1,93 @@
|
|||||||
import InfisicalClient from 'infisical-node';
|
import InfisicalClient from "infisical-node";
|
||||||
|
|
||||||
export const client = new InfisicalClient({
|
export const client = new InfisicalClient({
|
||||||
token: process.env.INFISICAL_TOKEN!
|
token: process.env.INFISICAL_TOKEN!,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const getPort = async () => (await client.getSecret('PORT')).secretValue || 4000;
|
export const getPort = async () => (await client.getSecret("PORT")).secretValue || 4000;
|
||||||
export const getEncryptionKey = async () => {
|
export const getEncryptionKey = async () => {
|
||||||
const secretValue = (await client.getSecret('ENCRYPTION_KEY')).secretValue;
|
const secretValue = (await client.getSecret("ENCRYPTION_KEY")).secretValue;
|
||||||
return secretValue === '' ? undefined : secretValue;
|
return secretValue === "" ? undefined : secretValue;
|
||||||
}
|
}
|
||||||
export const getRootEncryptionKey = async () => {
|
export const getRootEncryptionKey = async () => {
|
||||||
const secretValue = (await client.getSecret('ROOT_ENCRYPTION_KEY')).secretValue;
|
const secretValue = (await client.getSecret("ROOT_ENCRYPTION_KEY")).secretValue;
|
||||||
return secretValue === '' ? undefined : secretValue;
|
return secretValue === "" ? undefined : secretValue;
|
||||||
}
|
}
|
||||||
export const getInviteOnlySignup = async () => (await client.getSecret('INVITE_ONLY_SIGNUP')).secretValue === 'true'
|
export const getInviteOnlySignup = async () => (await client.getSecret("INVITE_ONLY_SIGNUP")).secretValue === "true"
|
||||||
export const getSaltRounds = async () => parseInt((await client.getSecret('SALT_ROUNDS')).secretValue) || 10;
|
export const getSaltRounds = async () => parseInt((await client.getSecret("SALT_ROUNDS")).secretValue) || 10;
|
||||||
export const getJwtAuthLifetime = async () => (await client.getSecret('JWT_AUTH_LIFETIME')).secretValue || '10d';
|
export const getJwtAuthLifetime = async () => (await client.getSecret("JWT_AUTH_LIFETIME")).secretValue || "10d";
|
||||||
export const getJwtAuthSecret = async () => (await client.getSecret('JWT_AUTH_SECRET')).secretValue;
|
export const getJwtAuthSecret = async () => (await client.getSecret("JWT_AUTH_SECRET")).secretValue;
|
||||||
export const getJwtMfaLifetime = async () => (await client.getSecret('JWT_MFA_LIFETIME')).secretValue || '5m';
|
export const getJwtMfaLifetime = async () => (await client.getSecret("JWT_MFA_LIFETIME")).secretValue || "5m";
|
||||||
export const getJwtMfaSecret = async () => (await client.getSecret('JWT_MFA_LIFETIME')).secretValue || '5m';
|
export const getJwtMfaSecret = async () => (await client.getSecret("JWT_MFA_LIFETIME")).secretValue || "5m";
|
||||||
export const getJwtRefreshLifetime = async () => (await client.getSecret('JWT_REFRESH_LIFETIME')).secretValue || '90d';
|
export const getJwtRefreshLifetime = async () => (await client.getSecret("JWT_REFRESH_LIFETIME")).secretValue || "90d";
|
||||||
export const getJwtRefreshSecret = async () => (await client.getSecret('JWT_REFRESH_SECRET')).secretValue;
|
export const getJwtRefreshSecret = async () => (await client.getSecret("JWT_REFRESH_SECRET")).secretValue;
|
||||||
export const getJwtServiceSecret = async () => (await client.getSecret('JWT_SERVICE_SECRET')).secretValue;
|
export const getJwtServiceSecret = async () => (await client.getSecret("JWT_SERVICE_SECRET")).secretValue;
|
||||||
export const getJwtSignupLifetime = async () => (await client.getSecret('JWT_SIGNUP_LIFETIME')).secretValue || '15m';
|
export const getJwtSignupLifetime = async () => (await client.getSecret("JWT_SIGNUP_LIFETIME")).secretValue || "15m";
|
||||||
export const getJwtProviderAuthSecret = async () => (await client.getSecret('JWT_PROVIDER_AUTH_SECRET')).secretValue;
|
export const getJwtProviderAuthSecret = async () => (await client.getSecret("JWT_PROVIDER_AUTH_SECRET")).secretValue;
|
||||||
export const getJwtProviderAuthLifetime = async () => (await client.getSecret('JWT_PROVIDER_AUTH_LIFETIME')).secretValue || '15m';
|
export const getJwtProviderAuthLifetime = async () => (await client.getSecret("JWT_PROVIDER_AUTH_LIFETIME")).secretValue || "15m";
|
||||||
export const getJwtSignupSecret = async () => (await client.getSecret('JWT_SIGNUP_SECRET')).secretValue;
|
export const getJwtSignupSecret = async () => (await client.getSecret("JWT_SIGNUP_SECRET")).secretValue;
|
||||||
export const getMongoURL = async () => (await client.getSecret('MONGO_URL')).secretValue;
|
export const getMongoURL = async () => (await client.getSecret("MONGO_URL")).secretValue;
|
||||||
export const getNodeEnv = async () => (await client.getSecret('NODE_ENV')).secretValue || 'production';
|
export const getNodeEnv = async () => (await client.getSecret("NODE_ENV")).secretValue || "production";
|
||||||
export const getVerboseErrorOutput = async () => (await client.getSecret('VERBOSE_ERROR_OUTPUT')).secretValue === 'true' && true;
|
export const getVerboseErrorOutput = async () => (await client.getSecret("VERBOSE_ERROR_OUTPUT")).secretValue === "true" && true;
|
||||||
export const getLokiHost = async () => (await client.getSecret('LOKI_HOST')).secretValue;
|
export const getLokiHost = async () => (await client.getSecret("LOKI_HOST")).secretValue;
|
||||||
export const getClientIdAzure = async () => (await client.getSecret('CLIENT_ID_AZURE')).secretValue;
|
export const getClientIdAzure = async () => (await client.getSecret("CLIENT_ID_AZURE")).secretValue;
|
||||||
export const getClientIdHeroku = async () => (await client.getSecret('CLIENT_ID_HEROKU')).secretValue;
|
export const getClientIdHeroku = async () => (await client.getSecret("CLIENT_ID_HEROKU")).secretValue;
|
||||||
export const getClientIdVercel = async () => (await client.getSecret('CLIENT_ID_VERCEL')).secretValue;
|
export const getClientIdVercel = async () => (await client.getSecret("CLIENT_ID_VERCEL")).secretValue;
|
||||||
export const getClientIdNetlify = async () => (await client.getSecret('CLIENT_ID_NETLIFY')).secretValue;
|
export const getClientIdNetlify = async () => (await client.getSecret("CLIENT_ID_NETLIFY")).secretValue;
|
||||||
export const getClientIdGitHub = async () => (await client.getSecret('CLIENT_ID_GITHUB')).secretValue;
|
export const getClientIdGitHub = async () => (await client.getSecret("CLIENT_ID_GITHUB")).secretValue;
|
||||||
export const getClientIdGitLab = async () => (await client.getSecret('CLIENT_ID_GITLAB')).secretValue;
|
export const getClientIdGitLab = async () => (await client.getSecret("CLIENT_ID_GITLAB")).secretValue;
|
||||||
export const getClientIdGoogle = async () => (await client.getSecret('CLIENT_ID_GOOGLE')).secretValue;
|
export const getClientIdGoogle = async () => (await client.getSecret("CLIENT_ID_GOOGLE")).secretValue;
|
||||||
export const getClientSecretAzure = async () => (await client.getSecret('CLIENT_SECRET_AZURE')).secretValue;
|
export const getClientSecretAzure = async () => (await client.getSecret("CLIENT_SECRET_AZURE")).secretValue;
|
||||||
export const getClientSecretHeroku = async () => (await client.getSecret('CLIENT_SECRET_HEROKU')).secretValue;
|
export const getClientSecretHeroku = async () => (await client.getSecret("CLIENT_SECRET_HEROKU")).secretValue;
|
||||||
export const getClientSecretVercel = async () => (await client.getSecret('CLIENT_SECRET_VERCEL')).secretValue;
|
export const getClientSecretVercel = async () => (await client.getSecret("CLIENT_SECRET_VERCEL")).secretValue;
|
||||||
export const getClientSecretNetlify = async () => (await client.getSecret('CLIENT_SECRET_NETLIFY')).secretValue;
|
export const getClientSecretNetlify = async () => (await client.getSecret("CLIENT_SECRET_NETLIFY")).secretValue;
|
||||||
export const getClientSecretGitHub = async () => (await client.getSecret('CLIENT_SECRET_GITHUB')).secretValue;
|
export const getClientSecretGitHub = async () => (await client.getSecret("CLIENT_SECRET_GITHUB")).secretValue;
|
||||||
export const getClientSecretGitLab = async () => (await client.getSecret('CLIENT_SECRET_GITLAB')).secretValue;
|
export const getClientSecretGitLab = async () => (await client.getSecret("CLIENT_SECRET_GITLAB")).secretValue;
|
||||||
export const getClientSecretGoogle = async () => (await client.getSecret('CLIENT_SECRET_GOOGLE')).secretValue;
|
export const getClientSecretGoogle = async () => (await client.getSecret("CLIENT_SECRET_GOOGLE")).secretValue;
|
||||||
export const getClientSlugVercel = async () => (await client.getSecret('CLIENT_SLUG_VERCEL')).secretValue;
|
export const getClientSlugVercel = async () => (await client.getSecret("CLIENT_SLUG_VERCEL")).secretValue;
|
||||||
export const getPostHogHost = async () => (await client.getSecret('POSTHOG_HOST')).secretValue || 'https://app.posthog.com';
|
export const getPostHogHost = async () => (await client.getSecret("POSTHOG_HOST")).secretValue || "https://app.posthog.com";
|
||||||
export const getPostHogProjectApiKey = async () => (await client.getSecret('POSTHOG_PROJECT_API_KEY')).secretValue || 'phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE';
|
export const getPostHogProjectApiKey = async () => (await client.getSecret("POSTHOG_PROJECT_API_KEY")).secretValue || "phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE";
|
||||||
export const getSentryDSN = async () => (await client.getSecret('SENTRY_DSN')).secretValue;
|
export const getSentryDSN = async () => (await client.getSecret("SENTRY_DSN")).secretValue;
|
||||||
export const getSiteURL = async () => (await client.getSecret('SITE_URL')).secretValue;
|
export const getSiteURL = async () => (await client.getSecret("SITE_URL")).secretValue;
|
||||||
export const getSmtpHost = async () => (await client.getSecret('SMTP_HOST')).secretValue;
|
export const getSmtpHost = async () => (await client.getSecret("SMTP_HOST")).secretValue;
|
||||||
export const getSmtpSecure = async () => (await client.getSecret('SMTP_SECURE')).secretValue === 'true' || false;
|
export const getSmtpSecure = async () => (await client.getSecret("SMTP_SECURE")).secretValue === "true" || false;
|
||||||
export const getSmtpPort = async () => parseInt((await client.getSecret('SMTP_PORT')).secretValue) || 587;
|
export const getSmtpPort = async () => parseInt((await client.getSecret("SMTP_PORT")).secretValue) || 587;
|
||||||
export const getSmtpUsername = async () => (await client.getSecret('SMTP_USERNAME')).secretValue;
|
export const getSmtpUsername = async () => (await client.getSecret("SMTP_USERNAME")).secretValue;
|
||||||
export const getSmtpPassword = async () => (await client.getSecret('SMTP_PASSWORD')).secretValue;
|
export const getSmtpPassword = async () => (await client.getSecret("SMTP_PASSWORD")).secretValue;
|
||||||
export const getSmtpFromAddress = async () => (await client.getSecret('SMTP_FROM_ADDRESS')).secretValue;
|
export const getSmtpFromAddress = async () => (await client.getSecret("SMTP_FROM_ADDRESS")).secretValue;
|
||||||
export const getSmtpFromName = async () => (await client.getSecret('SMTP_FROM_NAME')).secretValue || 'Infisical';
|
export const getSmtpFromName = async () => (await client.getSecret("SMTP_FROM_NAME")).secretValue || "Infisical";
|
||||||
|
|
||||||
export const getLicenseKey = async () => {
|
export const getLicenseKey = async () => {
|
||||||
const secretValue = (await client.getSecret('LICENSE_KEY')).secretValue;
|
const secretValue = (await client.getSecret("LICENSE_KEY")).secretValue;
|
||||||
return secretValue === '' ? undefined : secretValue;
|
return secretValue === "" ? undefined : secretValue;
|
||||||
}
|
}
|
||||||
export const getLicenseServerKey = async () => {
|
export const getLicenseServerKey = async () => {
|
||||||
const secretValue = (await client.getSecret('LICENSE_SERVER_KEY')).secretValue;
|
const secretValue = (await client.getSecret("LICENSE_SERVER_KEY")).secretValue;
|
||||||
return secretValue === '' ? undefined : secretValue;
|
return secretValue === "" ? undefined : secretValue;
|
||||||
}
|
}
|
||||||
export const getLicenseServerUrl = async () => (await client.getSecret('LICENSE_SERVER_URL')).secretValue || 'https://portal.infisical.com';
|
export const getLicenseServerUrl = async () => (await client.getSecret("LICENSE_SERVER_URL")).secretValue || "https://portal.infisical.com";
|
||||||
|
|
||||||
// TODO: deprecate from here
|
// TODO: deprecate from here
|
||||||
export const getStripeProductStarter = async () => (await client.getSecret('STRIPE_PRODUCT_STARTER')).secretValue;
|
export const getStripeProductStarter = async () => (await client.getSecret("STRIPE_PRODUCT_STARTER")).secretValue;
|
||||||
export const getStripeProductPro = async () => (await client.getSecret('STRIPE_PRODUCT_PRO')).secretValue;
|
export const getStripeProductPro = async () => (await client.getSecret("STRIPE_PRODUCT_PRO")).secretValue;
|
||||||
export const getStripeProductTeam = async () => (await client.getSecret('STRIPE_PRODUCT_TEAM')).secretValue;
|
export const getStripeProductTeam = async () => (await client.getSecret("STRIPE_PRODUCT_TEAM")).secretValue;
|
||||||
export const getStripePublishableKey = async () => (await client.getSecret('STRIPE_PUBLISHABLE_KEY')).secretValue;
|
export const getStripePublishableKey = async () => (await client.getSecret("STRIPE_PUBLISHABLE_KEY")).secretValue;
|
||||||
export const getStripeSecretKey = async () => (await client.getSecret('STRIPE_SECRET_KEY')).secretValue;
|
export const getStripeSecretKey = async () => (await client.getSecret("STRIPE_SECRET_KEY")).secretValue;
|
||||||
export const getStripeWebhookSecret = async () => (await client.getSecret('STRIPE_WEBHOOK_SECRET')).secretValue;
|
export const getStripeWebhookSecret = async () => (await client.getSecret("STRIPE_WEBHOOK_SECRET")).secretValue;
|
||||||
|
|
||||||
export const getTelemetryEnabled = async () => (await client.getSecret('TELEMETRY_ENABLED')).secretValue !== 'false' && true;
|
export const getTelemetryEnabled = async () => (await client.getSecret("TELEMETRY_ENABLED")).secretValue !== "false" && true;
|
||||||
export const getLoopsApiKey = async () => (await client.getSecret('LOOPS_API_KEY')).secretValue;
|
export const getLoopsApiKey = async () => (await client.getSecret("LOOPS_API_KEY")).secretValue;
|
||||||
export const getSmtpConfigured = async () => (await client.getSecret('SMTP_HOST')).secretValue == '' || (await client.getSecret('SMTP_HOST')).secretValue == undefined ? false : true
|
export const getSmtpConfigured = async () => (await client.getSecret("SMTP_HOST")).secretValue == "" || (await client.getSecret("SMTP_HOST")).secretValue == undefined ? false : true
|
||||||
export const getHttpsEnabled = async () => {
|
export const getHttpsEnabled = async () => {
|
||||||
if ((await getNodeEnv()) != "production") {
|
if ((await getNodeEnv()) != "production") {
|
||||||
// no https for anything other than prod
|
// no https for anything other than prod
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((await client.getSecret('HTTPS_ENABLED')).secretValue == undefined || (await client.getSecret('HTTPS_ENABLED')).secretValue == "") {
|
if ((await client.getSecret("HTTPS_ENABLED")).secretValue == undefined || (await client.getSecret("HTTPS_ENABLED")).secretValue == "") {
|
||||||
// default when no value present
|
// default when no value present
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return (await client.getSecret('HTTPS_ENABLED')).secretValue === 'true' && true
|
return (await client.getSecret("HTTPS_ENABLED")).secretValue === "true" && true
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
import axios from 'axios';
|
import axios from "axios";
|
||||||
import axiosRetry from 'axios-retry';
|
import axiosRetry from "axios-retry";
|
||||||
import {
|
import {
|
||||||
getLicenseServerKeyAuthToken,
|
|
||||||
setLicenseServerKeyAuthToken,
|
|
||||||
getLicenseKeyAuthToken,
|
getLicenseKeyAuthToken,
|
||||||
setLicenseKeyAuthToken
|
getLicenseServerKeyAuthToken,
|
||||||
} from './storage';
|
setLicenseKeyAuthToken,
|
||||||
|
setLicenseServerKeyAuthToken,
|
||||||
|
} from "./storage";
|
||||||
import {
|
import {
|
||||||
getLicenseKey,
|
getLicenseKey,
|
||||||
getLicenseServerKey,
|
getLicenseServerKey,
|
||||||
getLicenseServerUrl
|
getLicenseServerUrl,
|
||||||
} from './index';
|
} from "./index";
|
||||||
|
|
||||||
// should have JWT to interact with the license server
|
// should have JWT to interact with the license server
|
||||||
export const licenseServerKeyRequest = axios.create();
|
export const licenseServerKeyRequest = axios.create();
|
||||||
@@ -35,8 +35,8 @@ export const refreshLicenseServerKeyToken = async () => {
|
|||||||
`${licenseServerUrl}/api/auth/v1/license-server-login`, {},
|
`${licenseServerUrl}/api/auth/v1/license-server-login`, {},
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
'X-API-KEY': licenseServerKey
|
"X-API-KEY": licenseServerKey,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -53,8 +53,8 @@ export const refreshLicenseKeyToken = async () => {
|
|||||||
`${licenseServerUrl}/api/auth/v1/license-login`, {},
|
`${licenseServerUrl}/api/auth/v1/license-login`, {},
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
'X-API-KEY': licenseKey
|
"X-API-KEY": licenseKey,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ licenseServerKeyRequest.interceptors.response.use((response) => {
|
|||||||
// refresh
|
// refresh
|
||||||
const token = await refreshLicenseServerKeyToken();
|
const token = await refreshLicenseServerKeyToken();
|
||||||
|
|
||||||
axios.defaults.headers.common['Authorization'] = 'Bearer ' + token;
|
axios.defaults.headers.common["Authorization"] = "Bearer " + token;
|
||||||
return licenseServerKeyRequest(originalRequest);
|
return licenseServerKeyRequest(originalRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ licenseKeyRequest.interceptors.response.use((response) => {
|
|||||||
// refresh
|
// refresh
|
||||||
const token = await refreshLicenseKeyToken();
|
const token = await refreshLicenseKeyToken();
|
||||||
|
|
||||||
axios.defaults.headers.common['Authorization'] = 'Bearer ' + token;
|
axios.defaults.headers.common["Authorization"] = "Bearer " + token;
|
||||||
return licenseKeyRequest(originalRequest);
|
return licenseKeyRequest(originalRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const MemoryLicenseServerKeyTokenStorage = () => {
|
|||||||
setToken: (token: string) => {
|
setToken: (token: string) => {
|
||||||
authToken = token;
|
authToken = token;
|
||||||
},
|
},
|
||||||
getToken: () => authToken
|
getToken: () => authToken,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ const MemoryLicenseKeyTokenStorage = () => {
|
|||||||
setToken: (token: string) => {
|
setToken: (token: string) => {
|
||||||
authToken = token;
|
authToken = token;
|
||||||
},
|
},
|
||||||
getToken: () => authToken
|
getToken: () => authToken,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,36 +1,36 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import fs from 'fs';
|
import fs from "fs";
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from "jsonwebtoken";
|
||||||
import * as bigintConversion from 'bigint-conversion';
|
import * as bigintConversion from "bigint-conversion";
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const jsrp = require('jsrp');
|
const jsrp = require("jsrp");
|
||||||
import {
|
import {
|
||||||
User,
|
LoginSRPDetail,
|
||||||
LoginSRPDetail,
|
TokenVersion,
|
||||||
TokenVersion
|
User,
|
||||||
} from '../../models';
|
} from "../../models";
|
||||||
import { createToken, issueAuthTokens, clearTokens } from '../../helpers/auth';
|
import { clearTokens, createToken, issueAuthTokens } from "../../helpers/auth";
|
||||||
import { checkUserDevice } from '../../helpers/user';
|
import { checkUserDevice } from "../../helpers/user";
|
||||||
import {
|
import {
|
||||||
ACTION_LOGIN,
|
ACTION_LOGIN,
|
||||||
ACTION_LOGOUT,
|
ACTION_LOGOUT,
|
||||||
AUTH_MODE_JWT
|
AUTH_MODE_JWT,
|
||||||
} from '../../variables';
|
} from "../../variables";
|
||||||
import {
|
import {
|
||||||
BadRequestError,
|
BadRequestError,
|
||||||
UnauthorizedRequestError
|
UnauthorizedRequestError,
|
||||||
} from '../../utils/errors';
|
} from "../../utils/errors";
|
||||||
import { EELogService } from '../../ee/services';
|
import { EELogService } from "../../ee/services";
|
||||||
import { getChannelFromUserAgent } from '../../utils/posthog';
|
import { getChannelFromUserAgent } from "../../utils/posthog";
|
||||||
import {
|
import {
|
||||||
getJwtRefreshSecret,
|
getHttpsEnabled,
|
||||||
getJwtAuthLifetime,
|
getJwtAuthLifetime,
|
||||||
getJwtAuthSecret,
|
getJwtAuthSecret,
|
||||||
getHttpsEnabled
|
getJwtRefreshSecret,
|
||||||
} from '../../config';
|
} from "../../config";
|
||||||
|
|
||||||
declare module 'jsonwebtoken' {
|
declare module "jsonwebtoken" {
|
||||||
export interface UserIDJwtPayload extends jwt.JwtPayload {
|
export interface UserIDJwtPayload extends jwt.JwtPayload {
|
||||||
userId: string;
|
userId: string;
|
||||||
refreshVersion?: number;
|
refreshVersion?: number;
|
||||||
@@ -46,20 +46,20 @@ declare module 'jsonwebtoken' {
|
|||||||
export const login1 = async (req: Request, res: Response) => {
|
export const login1 = async (req: Request, res: Response) => {
|
||||||
const {
|
const {
|
||||||
email,
|
email,
|
||||||
clientPublicKey
|
clientPublicKey,
|
||||||
}: { email: string; clientPublicKey: string } = req.body;
|
}: { email: string; clientPublicKey: string } = req.body;
|
||||||
|
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
email
|
email,
|
||||||
}).select('+salt +verifier');
|
}).select("+salt +verifier");
|
||||||
|
|
||||||
if (!user) throw new Error('Failed to find user');
|
if (!user) throw new Error("Failed to find user");
|
||||||
|
|
||||||
const server = new jsrp.server();
|
const server = new jsrp.server();
|
||||||
server.init(
|
server.init(
|
||||||
{
|
{
|
||||||
salt: user.salt,
|
salt: user.salt,
|
||||||
verifier: user.verifier
|
verifier: user.verifier,
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
// generate server-side public key
|
// generate server-side public key
|
||||||
@@ -73,7 +73,7 @@ export const login1 = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
serverPublicKey,
|
serverPublicKey,
|
||||||
salt: user.salt
|
salt: user.salt,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -89,10 +89,10 @@ export const login1 = async (req: Request, res: Response) => {
|
|||||||
export const login2 = async (req: Request, res: Response) => {
|
export const login2 = async (req: Request, res: Response) => {
|
||||||
const { email, clientProof } = req.body;
|
const { email, clientProof } = req.body;
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
email
|
email,
|
||||||
}).select('+salt +verifier +publicKey +encryptedPrivateKey +iv +tag');
|
}).select("+salt +verifier +publicKey +encryptedPrivateKey +iv +tag");
|
||||||
|
|
||||||
if (!user) throw new Error('Failed to find user');
|
if (!user) throw new Error("Failed to find user");
|
||||||
|
|
||||||
const loginSRPDetailFromDB = await LoginSRPDetail.findOneAndDelete({ email: email })
|
const loginSRPDetailFromDB = await LoginSRPDetail.findOneAndDelete({ email: email })
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ export const login2 = async (req: Request, res: Response) => {
|
|||||||
{
|
{
|
||||||
salt: user.salt,
|
salt: user.salt,
|
||||||
verifier: user.verifier,
|
verifier: user.verifier,
|
||||||
b: loginSRPDetailFromDB.serverBInt
|
b: loginSRPDetailFromDB.serverBInt,
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
server.setClientPublicKey(loginSRPDetailFromDB.clientPublicKey);
|
server.setClientPublicKey(loginSRPDetailFromDB.clientPublicKey);
|
||||||
@@ -117,33 +117,33 @@ export const login2 = async (req: Request, res: Response) => {
|
|||||||
await checkUserDevice({
|
await checkUserDevice({
|
||||||
user,
|
user,
|
||||||
ip: req.realIP,
|
ip: req.realIP,
|
||||||
userAgent: req.headers['user-agent'] ?? ''
|
userAgent: req.headers["user-agent"] ?? "",
|
||||||
});
|
});
|
||||||
|
|
||||||
const tokens = await issueAuthTokens({
|
const tokens = await issueAuthTokens({
|
||||||
userId: user._id,
|
userId: user._id,
|
||||||
ip: req.realIP,
|
ip: req.realIP,
|
||||||
userAgent: req.headers['user-agent'] ?? ''
|
userAgent: req.headers["user-agent"] ?? "",
|
||||||
});
|
});
|
||||||
|
|
||||||
// store (refresh) token in httpOnly cookie
|
// store (refresh) token in httpOnly cookie
|
||||||
res.cookie('jid', tokens.refreshToken, {
|
res.cookie("jid", tokens.refreshToken, {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
path: '/',
|
path: "/",
|
||||||
sameSite: 'strict',
|
sameSite: "strict",
|
||||||
secure: await getHttpsEnabled()
|
secure: await getHttpsEnabled(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const loginAction = await EELogService.createAction({
|
const loginAction = await EELogService.createAction({
|
||||||
name: ACTION_LOGIN,
|
name: ACTION_LOGIN,
|
||||||
userId: user._id
|
userId: user._id,
|
||||||
});
|
});
|
||||||
|
|
||||||
loginAction && await EELogService.createLog({
|
loginAction && await EELogService.createLog({
|
||||||
userId: user._id,
|
userId: user._id,
|
||||||
actions: [loginAction],
|
actions: [loginAction],
|
||||||
channel: getChannelFromUserAgent(req.headers['user-agent']),
|
channel: getChannelFromUserAgent(req.headers["user-agent"]),
|
||||||
ipAddress: req.realIP
|
ipAddress: req.realIP,
|
||||||
});
|
});
|
||||||
|
|
||||||
// return (access) token in response
|
// return (access) token in response
|
||||||
@@ -152,12 +152,12 @@ export const login2 = async (req: Request, res: Response) => {
|
|||||||
publicKey: user.publicKey,
|
publicKey: user.publicKey,
|
||||||
encryptedPrivateKey: user.encryptedPrivateKey,
|
encryptedPrivateKey: user.encryptedPrivateKey,
|
||||||
iv: user.iv,
|
iv: user.iv,
|
||||||
tag: user.tag
|
tag: user.tag,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
message: 'Failed to authenticate. Try again?'
|
message: "Failed to authenticate. Try again?",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -175,51 +175,51 @@ export const logout = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// clear httpOnly cookie
|
// clear httpOnly cookie
|
||||||
res.cookie('jid', '', {
|
res.cookie("jid", "", {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
path: '/',
|
path: "/",
|
||||||
sameSite: 'strict',
|
sameSite: "strict",
|
||||||
secure: (await getHttpsEnabled()) as boolean
|
secure: (await getHttpsEnabled()) as boolean,
|
||||||
});
|
});
|
||||||
|
|
||||||
const logoutAction = await EELogService.createAction({
|
const logoutAction = await EELogService.createAction({
|
||||||
name: ACTION_LOGOUT,
|
name: ACTION_LOGOUT,
|
||||||
userId: req.user._id
|
userId: req.user._id,
|
||||||
});
|
});
|
||||||
|
|
||||||
logoutAction && await EELogService.createLog({
|
logoutAction && await EELogService.createLog({
|
||||||
userId: req.user._id,
|
userId: req.user._id,
|
||||||
actions: [logoutAction],
|
actions: [logoutAction],
|
||||||
channel: getChannelFromUserAgent(req.headers['user-agent']),
|
channel: getChannelFromUserAgent(req.headers["user-agent"]),
|
||||||
ipAddress: req.realIP
|
ipAddress: req.realIP,
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfully logged out.'
|
message: "Successfully logged out.",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getCommonPasswords = async (req: Request, res: Response) => {
|
export const getCommonPasswords = async (req: Request, res: Response) => {
|
||||||
const commonPasswords = fs.readFileSync(
|
const commonPasswords = fs.readFileSync(
|
||||||
path.resolve(__dirname, '../../data/' + 'common_passwords.txt'),
|
path.resolve(__dirname, "../../data/" + "common_passwords.txt"),
|
||||||
'utf8'
|
"utf8"
|
||||||
).split('\n');
|
).split("\n");
|
||||||
|
|
||||||
return res.status(200).send(commonPasswords);
|
return res.status(200).send(commonPasswords);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const revokeAllSessions = async (req: Request, res: Response) => {
|
export const revokeAllSessions = async (req: Request, res: Response) => {
|
||||||
await TokenVersion.updateMany({
|
await TokenVersion.updateMany({
|
||||||
user: req.user._id
|
user: req.user._id,
|
||||||
}, {
|
}, {
|
||||||
$inc: {
|
$inc: {
|
||||||
refreshVersion: 1,
|
refreshVersion: 1,
|
||||||
accessVersion: 1
|
accessVersion: 1,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfully revoked all sessions.'
|
message: "Successfully revoked all sessions.",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +231,7 @@ export const revokeAllSessions = async (req: Request, res: Response) => {
|
|||||||
*/
|
*/
|
||||||
export const checkAuth = async (req: Request, res: Response) => {
|
export const checkAuth = async (req: Request, res: Response) => {
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Authenticated'
|
message: "Authenticated",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,7 +245,7 @@ export const getNewToken = async (req: Request, res: Response) => {
|
|||||||
const refreshToken = req.cookies.jid;
|
const refreshToken = req.cookies.jid;
|
||||||
|
|
||||||
if (!refreshToken) {
|
if (!refreshToken) {
|
||||||
throw new Error('Failed to find refresh token in request cookies');
|
throw new Error("Failed to find refresh token in request cookies");
|
||||||
}
|
}
|
||||||
|
|
||||||
const decodedToken = <jwt.UserIDJwtPayload>(
|
const decodedToken = <jwt.UserIDJwtPayload>(
|
||||||
@@ -253,35 +253,35 @@ export const getNewToken = async (req: Request, res: Response) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
_id: decodedToken.userId
|
_id: decodedToken.userId,
|
||||||
}).select('+publicKey +refreshVersion +accessVersion');
|
}).select("+publicKey +refreshVersion +accessVersion");
|
||||||
|
|
||||||
if (!user) throw new Error('Failed to authenticate unfound user');
|
if (!user) throw new Error("Failed to authenticate unfound user");
|
||||||
if (!user?.publicKey)
|
if (!user?.publicKey)
|
||||||
throw new Error('Failed to authenticate not fully set up account');
|
throw new Error("Failed to authenticate not fully set up account");
|
||||||
|
|
||||||
const tokenVersion = await TokenVersion.findById(decodedToken.tokenVersionId);
|
const tokenVersion = await TokenVersion.findById(decodedToken.tokenVersionId);
|
||||||
|
|
||||||
if (!tokenVersion) throw UnauthorizedRequestError({
|
if (!tokenVersion) throw UnauthorizedRequestError({
|
||||||
message: 'Failed to validate refresh token'
|
message: "Failed to validate refresh token",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (decodedToken.refreshVersion !== tokenVersion.refreshVersion) throw BadRequestError({
|
if (decodedToken.refreshVersion !== tokenVersion.refreshVersion) throw BadRequestError({
|
||||||
message: 'Failed to validate refresh token'
|
message: "Failed to validate refresh token",
|
||||||
});
|
});
|
||||||
|
|
||||||
const token = createToken({
|
const token = createToken({
|
||||||
payload: {
|
payload: {
|
||||||
userId: decodedToken.userId,
|
userId: decodedToken.userId,
|
||||||
tokenVersionId: tokenVersion._id.toString(),
|
tokenVersionId: tokenVersion._id.toString(),
|
||||||
accessVersion: tokenVersion.refreshVersion
|
accessVersion: tokenVersion.refreshVersion,
|
||||||
},
|
},
|
||||||
expiresIn: await getJwtAuthLifetime(),
|
expiresIn: await getJwtAuthLifetime(),
|
||||||
secret: await getJwtAuthSecret()
|
secret: await getJwtAuthSecret(),
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
token
|
token,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import { Types } from 'mongoose';
|
import { Types } from "mongoose";
|
||||||
import { Bot, BotKey } from '../../models';
|
import { Bot, BotKey } from "../../models";
|
||||||
import { createBot } from '../../helpers/bot';
|
import { createBot } from "../../helpers/bot";
|
||||||
|
|
||||||
interface BotKey {
|
interface BotKey {
|
||||||
encryptedKey: string;
|
encryptedKey: string;
|
||||||
@@ -19,20 +19,20 @@ export const getBotByWorkspaceId = async (req: Request, res: Response) => {
|
|||||||
const { workspaceId } = req.params;
|
const { workspaceId } = req.params;
|
||||||
|
|
||||||
let bot = await Bot.findOne({
|
let bot = await Bot.findOne({
|
||||||
workspace: workspaceId
|
workspace: workspaceId,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!bot) {
|
if (!bot) {
|
||||||
// case: bot doesn't exist for workspace with id [workspaceId]
|
// case: bot doesn't exist for workspace with id [workspaceId]
|
||||||
// -> create a new bot and return it
|
// -> create a new bot and return it
|
||||||
bot = await createBot({
|
bot = await createBot({
|
||||||
name: 'Infisical Bot',
|
name: "Infisical Bot",
|
||||||
workspaceId: new Types.ObjectId(workspaceId)
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
bot
|
bot,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -49,40 +49,40 @@ export const setBotActiveState = async (req: Request, res: Response) => {
|
|||||||
// bot state set to active -> share workspace key with bot
|
// bot state set to active -> share workspace key with bot
|
||||||
if (!botKey?.encryptedKey || !botKey?.nonce) {
|
if (!botKey?.encryptedKey || !botKey?.nonce) {
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
message: 'Failed to set bot state to active - missing bot key'
|
message: "Failed to set bot state to active - missing bot key",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await BotKey.findOneAndUpdate({
|
await BotKey.findOneAndUpdate({
|
||||||
workspace: req.bot.workspace
|
workspace: req.bot.workspace,
|
||||||
}, {
|
}, {
|
||||||
encryptedKey: botKey.encryptedKey,
|
encryptedKey: botKey.encryptedKey,
|
||||||
nonce: botKey.nonce,
|
nonce: botKey.nonce,
|
||||||
sender: req.user._id,
|
sender: req.user._id,
|
||||||
bot: req.bot._id,
|
bot: req.bot._id,
|
||||||
workspace: req.bot.workspace
|
workspace: req.bot.workspace,
|
||||||
}, {
|
}, {
|
||||||
upsert: true,
|
upsert: true,
|
||||||
new: true
|
new: true,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// case: bot state set to inactive -> delete bot's workspace key
|
// case: bot state set to inactive -> delete bot's workspace key
|
||||||
await BotKey.deleteOne({
|
await BotKey.deleteOne({
|
||||||
bot: req.bot._id
|
bot: req.bot._id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let bot = await Bot.findOneAndUpdate({
|
const bot = await Bot.findOneAndUpdate({
|
||||||
_id: req.bot._id
|
_id: req.bot._id,
|
||||||
}, {
|
}, {
|
||||||
isActive
|
isActive,
|
||||||
}, {
|
}, {
|
||||||
new: true
|
new: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!bot) throw new Error('Failed to update bot active state');
|
if (!bot) throw new Error("Failed to update bot active state");
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
bot
|
bot,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import * as authController from './authController';
|
import * as authController from "./authController";
|
||||||
import * as botController from './botController';
|
import * as botController from "./botController";
|
||||||
import * as integrationAuthController from './integrationAuthController';
|
import * as integrationAuthController from "./integrationAuthController";
|
||||||
import * as integrationController from './integrationController';
|
import * as integrationController from "./integrationController";
|
||||||
import * as keyController from './keyController';
|
import * as keyController from "./keyController";
|
||||||
import * as membershipController from './membershipController';
|
import * as membershipController from "./membershipController";
|
||||||
import * as membershipOrgController from './membershipOrgController';
|
import * as membershipOrgController from "./membershipOrgController";
|
||||||
import * as organizationController from './organizationController';
|
import * as organizationController from "./organizationController";
|
||||||
import * as passwordController from './passwordController';
|
import * as passwordController from "./passwordController";
|
||||||
import * as secretController from './secretController';
|
import * as secretController from "./secretController";
|
||||||
import * as serviceTokenController from './serviceTokenController';
|
import * as serviceTokenController from "./serviceTokenController";
|
||||||
import * as signupController from './signupController';
|
import * as signupController from "./signupController";
|
||||||
import * as stripeController from './stripeController';
|
import * as stripeController from "./stripeController";
|
||||||
import * as userActionController from './userActionController';
|
import * as userActionController from "./userActionController";
|
||||||
import * as userController from './userController';
|
import * as userController from "./userController";
|
||||||
import * as workspaceController from './workspaceController';
|
import * as workspaceController from "./workspaceController";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
authController,
|
authController,
|
||||||
@@ -31,5 +31,5 @@ export {
|
|||||||
stripeController,
|
stripeController,
|
||||||
userActionController,
|
userActionController,
|
||||||
userController,
|
userController,
|
||||||
workspaceController
|
workspaceController,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,21 +1,17 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import { Types } from 'mongoose';
|
import { Types } from "mongoose";
|
||||||
|
import { standardRequest } from "../../config/request";
|
||||||
|
import { getApps, getTeams, revokeAccess } from "../../integrations";
|
||||||
|
import { Bot, IntegrationAuth } from "../../models";
|
||||||
|
import { IntegrationService } from "../../services";
|
||||||
import {
|
import {
|
||||||
IntegrationAuth,
|
ALGORITHM_AES_256_GCM,
|
||||||
Bot
|
ENCODING_SCHEME_UTF8,
|
||||||
} from '../../models';
|
INTEGRATION_RAILWAY_API_URL,
|
||||||
import { ALGORITHM_AES_256_GCM, ENCODING_SCHEME_UTF8, INTEGRATION_SET, getIntegrationOptions as getIntegrationOptionsFunc } from '../../variables';
|
INTEGRATION_SET,
|
||||||
import { IntegrationService } from '../../services';
|
INTEGRATION_VERCEL_API_URL,
|
||||||
import {
|
getIntegrationOptions as getIntegrationOptionsFunc
|
||||||
getApps,
|
} from "../../variables";
|
||||||
getTeams,
|
|
||||||
revokeAccess
|
|
||||||
} from '../../integrations';
|
|
||||||
import {
|
|
||||||
INTEGRATION_VERCEL_API_URL,
|
|
||||||
INTEGRATION_RAILWAY_API_URL
|
|
||||||
} from '../../variables';
|
|
||||||
import { standardRequest } from '../../config/request';
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Return integration authorization with id [integrationAuthId]
|
* Return integration authorization with id [integrationAuthId]
|
||||||
@@ -23,22 +19,23 @@ import { standardRequest } from '../../config/request';
|
|||||||
export const getIntegrationAuth = async (req: Request, res: Response) => {
|
export const getIntegrationAuth = async (req: Request, res: Response) => {
|
||||||
const { integrationAuthId } = req.params;
|
const { integrationAuthId } = req.params;
|
||||||
const integrationAuth = await IntegrationAuth.findById(integrationAuthId);
|
const integrationAuth = await IntegrationAuth.findById(integrationAuthId);
|
||||||
|
|
||||||
if (!integrationAuth) return res.status(400).send({
|
|
||||||
message: 'Failed to find integration authorization'
|
|
||||||
});
|
|
||||||
|
|
||||||
return res.status(200).send({
|
if (!integrationAuth)
|
||||||
integrationAuth
|
return res.status(400).send({
|
||||||
});
|
message: "Failed to find integration authorization"
|
||||||
}
|
});
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
integrationAuth
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const getIntegrationOptions = async (req: Request, res: Response) => {
|
export const getIntegrationOptions = async (req: Request, res: Response) => {
|
||||||
const INTEGRATION_OPTIONS = await getIntegrationOptionsFunc();
|
const INTEGRATION_OPTIONS = await getIntegrationOptionsFunc();
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
integrationOptions: INTEGRATION_OPTIONS,
|
integrationOptions: INTEGRATION_OPTIONS
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,26 +44,22 @@ export const getIntegrationOptions = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const oAuthExchange = async (
|
export const oAuthExchange = async (req: Request, res: Response) => {
|
||||||
req: Request,
|
|
||||||
res: Response
|
|
||||||
) => {
|
|
||||||
const { workspaceId, code, integration } = req.body;
|
const { workspaceId, code, integration } = req.body;
|
||||||
if (!INTEGRATION_SET.has(integration))
|
if (!INTEGRATION_SET.has(integration)) throw new Error("Failed to validate integration");
|
||||||
throw new Error('Failed to validate integration');
|
|
||||||
|
|
||||||
const environments = req.membership.workspace?.environments || [];
|
const environments = req.membership.workspace?.environments || [];
|
||||||
if(environments.length === 0){
|
if (environments.length === 0) {
|
||||||
throw new Error("Failed to get environments")
|
throw new Error("Failed to get environments");
|
||||||
}
|
}
|
||||||
|
|
||||||
const integrationAuth = await IntegrationService.handleOAuthExchange({
|
const integrationAuth = await IntegrationService.handleOAuthExchange({
|
||||||
workspaceId,
|
workspaceId,
|
||||||
integration,
|
integration,
|
||||||
code,
|
code,
|
||||||
environment: environments[0].slug,
|
environment: environments[0].slug
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
integrationAuth
|
integrationAuth
|
||||||
});
|
});
|
||||||
@@ -75,69 +68,70 @@ export const oAuthExchange = async (
|
|||||||
/**
|
/**
|
||||||
* Save integration access token and (optionally) access id as part of integration
|
* Save integration access token and (optionally) access id as part of integration
|
||||||
* [integration] for workspace with id [workspaceId]
|
* [integration] for workspace with id [workspaceId]
|
||||||
* @param req
|
* @param req
|
||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const saveIntegrationAccessToken = async (
|
export const saveIntegrationAccessToken = async (req: Request, res: Response) => {
|
||||||
req: Request,
|
// TODO: refactor
|
||||||
res: Response
|
// TODO: check if access token is valid for each integration
|
||||||
) => {
|
|
||||||
// TODO: refactor
|
|
||||||
// TODO: check if access token is valid for each integration
|
|
||||||
|
|
||||||
let integrationAuth;
|
let integrationAuth;
|
||||||
const {
|
const {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
accessId,
|
accessId,
|
||||||
accessToken,
|
accessToken,
|
||||||
url,
|
url,
|
||||||
namespace,
|
namespace,
|
||||||
integration
|
integration
|
||||||
}: {
|
}: {
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
accessId: string | null;
|
accessId: string | null;
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
url: string;
|
url: string;
|
||||||
namespace: string;
|
namespace: string;
|
||||||
integration: string;
|
integration: string;
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
const bot = await Bot.findOne({
|
const bot = await Bot.findOne({
|
||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
isActive: true
|
isActive: true
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!bot) throw new Error('Bot must be enabled to save integration access token');
|
|
||||||
|
|
||||||
integrationAuth = await IntegrationAuth.findOneAndUpdate({
|
if (!bot) throw new Error("Bot must be enabled to save integration access token");
|
||||||
workspace: new Types.ObjectId(workspaceId),
|
|
||||||
integration
|
integrationAuth = await IntegrationAuth.findOneAndUpdate(
|
||||||
}, {
|
{
|
||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
integration,
|
integration
|
||||||
url,
|
},
|
||||||
namespace,
|
{
|
||||||
algorithm: ALGORITHM_AES_256_GCM,
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
keyEncoding: ENCODING_SCHEME_UTF8
|
integration,
|
||||||
}, {
|
url,
|
||||||
new: true,
|
namespace,
|
||||||
upsert: true
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
});
|
keyEncoding: ENCODING_SCHEME_UTF8
|
||||||
|
},
|
||||||
// encrypt and save integration access details
|
{
|
||||||
integrationAuth = await IntegrationService.setIntegrationAuthAccess({
|
new: true,
|
||||||
integrationAuthId: integrationAuth._id.toString(),
|
upsert: true
|
||||||
accessId,
|
}
|
||||||
accessToken,
|
);
|
||||||
accessExpiresAt: undefined
|
|
||||||
});
|
// encrypt and save integration access details
|
||||||
|
integrationAuth = await IntegrationService.setIntegrationAuthAccess({
|
||||||
if (!integrationAuth) throw new Error('Failed to save integration access token');
|
integrationAuthId: integrationAuth._id.toString(),
|
||||||
|
accessId,
|
||||||
return res.status(200).send({
|
accessToken,
|
||||||
integrationAuth
|
accessExpiresAt: undefined
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
if (!integrationAuth) throw new Error("Failed to save integration access token");
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
integrationAuth
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return list of applications allowed for integration with integration authorization id [integrationAuthId]
|
* Return list of applications allowed for integration with integration authorization id [integrationAuthId]
|
||||||
@@ -147,108 +141,108 @@ export const saveIntegrationAccessToken = async (
|
|||||||
*/
|
*/
|
||||||
export const getIntegrationAuthApps = async (req: Request, res: Response) => {
|
export const getIntegrationAuthApps = async (req: Request, res: Response) => {
|
||||||
const teamId = req.query.teamId as string;
|
const teamId = req.query.teamId as string;
|
||||||
|
|
||||||
const apps = await getApps({
|
const apps = await getApps({
|
||||||
integrationAuth: req.integrationAuth,
|
integrationAuth: req.integrationAuth,
|
||||||
accessToken: req.accessToken,
|
accessToken: req.accessToken,
|
||||||
accessId: req.accessId,
|
accessId: req.accessId,
|
||||||
...teamId && { teamId }
|
...(teamId && { teamId })
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
apps
|
apps
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return list of teams allowed for integration with integration authorization id [integrationAuthId]
|
* Return list of teams allowed for integration with integration authorization id [integrationAuthId]
|
||||||
* @param req
|
* @param req
|
||||||
* @param res
|
* @param res
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getIntegrationAuthTeams = async (req: Request, res: Response) => {
|
export const getIntegrationAuthTeams = async (req: Request, res: Response) => {
|
||||||
const teams = await getTeams({
|
const teams = await getTeams({
|
||||||
integrationAuth: req.integrationAuth,
|
integrationAuth: req.integrationAuth,
|
||||||
accessToken: req.accessToken
|
accessToken: req.accessToken
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
teams
|
teams
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return list of available Vercel (preview) branches for Vercel project with
|
* Return list of available Vercel (preview) branches for Vercel project with
|
||||||
* id [appId]
|
* id [appId]
|
||||||
* @param req
|
* @param req
|
||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const getIntegrationAuthVercelBranches = async (req: Request, res: Response) => {
|
export const getIntegrationAuthVercelBranches = async (req: Request, res: Response) => {
|
||||||
const { integrationAuthId } = req.params;
|
const appId = req.query.appId as string;
|
||||||
const appId = req.query.appId as string;
|
|
||||||
|
|
||||||
interface VercelBranch {
|
|
||||||
ref: string;
|
|
||||||
lastCommit: string;
|
|
||||||
isProtected: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const params = new URLSearchParams({
|
interface VercelBranch {
|
||||||
projectId: appId,
|
ref: string;
|
||||||
...(req.integrationAuth.teamId ? {
|
lastCommit: string;
|
||||||
teamId: req.integrationAuth.teamId
|
isProtected: boolean;
|
||||||
} : {})
|
}
|
||||||
});
|
|
||||||
|
|
||||||
let branches: string[] = [];
|
const params = new URLSearchParams({
|
||||||
|
projectId: appId,
|
||||||
if (appId && appId !== '') {
|
...(req.integrationAuth.teamId
|
||||||
const { data }: { data: VercelBranch[] } = await standardRequest.get(
|
? {
|
||||||
`${INTEGRATION_VERCEL_API_URL}/v1/integrations/git-branches`,
|
teamId: req.integrationAuth.teamId
|
||||||
{
|
}
|
||||||
params,
|
: {})
|
||||||
headers: {
|
});
|
||||||
Authorization: `Bearer ${req.accessToken}`,
|
|
||||||
'Accept-Encoding': 'application/json'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
branches = data.map((b) => b.ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.status(200).send({
|
let branches: string[] = [];
|
||||||
branches
|
|
||||||
});
|
if (appId && appId !== "") {
|
||||||
}
|
const { data }: { data: VercelBranch[] } = await standardRequest.get(
|
||||||
|
`${INTEGRATION_VERCEL_API_URL}/v1/integrations/git-branches`,
|
||||||
|
{
|
||||||
|
params,
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${req.accessToken}`,
|
||||||
|
"Accept-Encoding": "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
branches = data.map((b) => b.ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
branches
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return list of Railway environments for Railway project with
|
* Return list of Railway environments for Railway project with
|
||||||
* id [appId]
|
* id [appId]
|
||||||
* @param req
|
* @param req
|
||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const getIntegrationAuthRailwayEnvironments = async (req: Request, res: Response) => {
|
export const getIntegrationAuthRailwayEnvironments = async (req: Request, res: Response) => {
|
||||||
const { integrationAuthId } = req.params;
|
const appId = req.query.appId as string;
|
||||||
const appId = req.query.appId as string;
|
|
||||||
|
|
||||||
interface RailwayEnvironment {
|
|
||||||
node: {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
isEphemeral: boolean;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Environment {
|
|
||||||
environmentId: string;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
let environments: Environment[] = [];
|
|
||||||
|
|
||||||
if (appId && appId !== '') {
|
interface RailwayEnvironment {
|
||||||
const query = `
|
node: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
isEphemeral: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Environment {
|
||||||
|
environmentId: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
let environments: Environment[] = [];
|
||||||
|
|
||||||
|
if (appId && appId !== "") {
|
||||||
|
const query = `
|
||||||
query GetEnvironments($projectId: String!, $after: String, $before: String, $first: Int, $isEphemeral: Boolean, $last: Int) {
|
query GetEnvironments($projectId: String!, $after: String, $before: String, $first: Int, $isEphemeral: Boolean, $last: Int) {
|
||||||
environments(projectId: $projectId, after: $after, before: $before, first: $first, isEphemeral: $isEphemeral, last: $last) {
|
environments(projectId: $projectId, after: $after, before: $before, first: $first, isEphemeral: $isEphemeral, last: $last) {
|
||||||
edges {
|
edges {
|
||||||
@@ -261,59 +255,68 @@ export const getIntegrationAuthRailwayEnvironments = async (req: Request, res: R
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const variables = {
|
const variables = {
|
||||||
projectId: appId
|
projectId: appId
|
||||||
}
|
};
|
||||||
|
|
||||||
const { data: { data: { environments: { edges } } } } = await standardRequest.post(INTEGRATION_RAILWAY_API_URL, {
|
const {
|
||||||
query,
|
data: {
|
||||||
variables,
|
data: {
|
||||||
}, {
|
environments: { edges }
|
||||||
headers: {
|
}
|
||||||
'Authorization': `Bearer ${req.accessToken}`,
|
}
|
||||||
'Content-Type': 'application/json',
|
} = await standardRequest.post(
|
||||||
},
|
INTEGRATION_RAILWAY_API_URL,
|
||||||
});
|
{
|
||||||
|
query,
|
||||||
environments = edges.map((e: RailwayEnvironment) => {
|
variables
|
||||||
return ({
|
},
|
||||||
name: e.node.name,
|
{
|
||||||
environmentId: e.node.id
|
headers: {
|
||||||
});
|
Authorization: `Bearer ${req.accessToken}`,
|
||||||
});
|
"Content-Type": "application/json"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return res.status(200).send({
|
);
|
||||||
environments
|
|
||||||
});
|
environments = edges.map((e: RailwayEnvironment) => {
|
||||||
}
|
return {
|
||||||
|
name: e.node.name,
|
||||||
|
environmentId: e.node.id
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
environments
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return list of Railway services for Railway project with id
|
* Return list of Railway services for Railway project with id
|
||||||
* [appId]
|
* [appId]
|
||||||
* @param req
|
* @param req
|
||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const getIntegrationAuthRailwayServices = async (req: Request, res: Response) => {
|
export const getIntegrationAuthRailwayServices = async (req: Request, res: Response) => {
|
||||||
const { integrationAuthId } = req.params;
|
const appId = req.query.appId as string;
|
||||||
const appId = req.query.appId as string;
|
|
||||||
|
interface RailwayService {
|
||||||
interface RailwayService {
|
node: {
|
||||||
node: {
|
id: string;
|
||||||
id: string;
|
name: string;
|
||||||
name: string;
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
interface Service {
|
||||||
interface Service {
|
name: string;
|
||||||
name: string;
|
serviceId: string;
|
||||||
serviceId: string;
|
}
|
||||||
}
|
|
||||||
|
let services: Service[] = [];
|
||||||
let services: Service[] = [];
|
|
||||||
|
const query = `
|
||||||
const query = `
|
|
||||||
query project($id: String!) {
|
query project($id: String!) {
|
||||||
project(id: $id) {
|
project(id: $id) {
|
||||||
createdAt
|
createdAt
|
||||||
@@ -341,31 +344,43 @@ export const getIntegrationAuthRailwayServices = async (req: Request, res: Respo
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
if (appId && appId !== '') {
|
if (appId && appId !== "") {
|
||||||
const variables = {
|
const variables = {
|
||||||
id: appId
|
id: appId
|
||||||
}
|
};
|
||||||
|
|
||||||
const { data: { data: { project: { services: { edges } } } } } = await standardRequest.post(INTEGRATION_RAILWAY_API_URL, {
|
const {
|
||||||
query,
|
data: {
|
||||||
variables
|
data: {
|
||||||
}, {
|
project: {
|
||||||
headers: {
|
services: { edges }
|
||||||
'Authorization': `Bearer ${req.accessToken}`,
|
}
|
||||||
'Content-Type': 'application/json',
|
}
|
||||||
},
|
}
|
||||||
});
|
} = await standardRequest.post(
|
||||||
|
INTEGRATION_RAILWAY_API_URL,
|
||||||
services = edges.map((e: RailwayService) => ({
|
{
|
||||||
name: e.node.name,
|
query,
|
||||||
serviceId: e.node.id
|
variables
|
||||||
}));
|
},
|
||||||
}
|
{
|
||||||
|
headers: {
|
||||||
return res.status(200).send({
|
Authorization: `Bearer ${req.accessToken}`,
|
||||||
services
|
"Content-Type": "application/json"
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
services = edges.map((e: RailwayService) => ({
|
||||||
|
name: e.node.name,
|
||||||
|
serviceId: e.node.id
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
services
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete integration authorization with id [integrationAuthId]
|
* Delete integration authorization with id [integrationAuthId]
|
||||||
@@ -376,10 +391,10 @@ export const getIntegrationAuthRailwayServices = async (req: Request, res: Respo
|
|||||||
export const deleteIntegrationAuth = async (req: Request, res: Response) => {
|
export const deleteIntegrationAuth = async (req: Request, res: Response) => {
|
||||||
const integrationAuth = await revokeAccess({
|
const integrationAuth = await revokeAccess({
|
||||||
integrationAuth: req.integrationAuth,
|
integrationAuth: req.integrationAuth,
|
||||||
accessToken: req.accessToken,
|
accessToken: req.accessToken
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
integrationAuth,
|
integrationAuth
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import { Key } from '../../models';
|
import { Key } from "../../models";
|
||||||
import { findMembership } from '../../helpers/membership';
|
import { findMembership } from "../../helpers/membership";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add (encrypted) copy of workspace key for workspace with id [workspaceId] for user with
|
* Add (encrypted) copy of workspace key for workspace with id [workspaceId] for user with
|
||||||
@@ -16,11 +16,11 @@ export const uploadKey = async (req: Request, res: Response) => {
|
|||||||
// validate membership of receiver
|
// validate membership of receiver
|
||||||
const receiverMembership = await findMembership({
|
const receiverMembership = await findMembership({
|
||||||
user: key.userId,
|
user: key.userId,
|
||||||
workspace: workspaceId
|
workspace: workspaceId,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!receiverMembership) {
|
if (!receiverMembership) {
|
||||||
throw new Error('Failed receiver membership validation for workspace');
|
throw new Error("Failed receiver membership validation for workspace");
|
||||||
}
|
}
|
||||||
|
|
||||||
await new Key({
|
await new Key({
|
||||||
@@ -28,11 +28,11 @@ export const uploadKey = async (req: Request, res: Response) => {
|
|||||||
nonce: key.nonce,
|
nonce: key.nonce,
|
||||||
sender: req.user._id,
|
sender: req.user._id,
|
||||||
receiver: key.userId,
|
receiver: key.userId,
|
||||||
workspace: workspaceId
|
workspace: workspaceId,
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfully uploaded key to workspace'
|
message: "Successfully uploaded key to workspace",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -48,16 +48,16 @@ export const getLatestKey = async (req: Request, res: Response) => {
|
|||||||
// get latest key
|
// get latest key
|
||||||
const latestKey = await Key.find({
|
const latestKey = await Key.find({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
receiver: req.user._id
|
receiver: req.user._id,
|
||||||
})
|
})
|
||||||
.sort({ createdAt: -1 })
|
.sort({ createdAt: -1 })
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.populate('sender', '+publicKey');
|
.populate("sender", "+publicKey");
|
||||||
|
|
||||||
const resObj: any = {};
|
const resObj: any = {};
|
||||||
|
|
||||||
if (latestKey.length > 0) {
|
if (latestKey.length > 0) {
|
||||||
resObj['latestKey'] = latestKey[0];
|
resObj["latestKey"] = latestKey[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send(resObj);
|
return res.status(200).send(resObj);
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import { Membership, MembershipOrg, User, Key } from '../../models';
|
import { Key, Membership, MembershipOrg, User } from "../../models";
|
||||||
import {
|
import { deleteMembership as deleteMember, findMembership } from "../../helpers/membership";
|
||||||
findMembership,
|
import { sendMail } from "../../helpers/nodemailer";
|
||||||
deleteMembership as deleteMember
|
import { ACCEPTED, ADMIN, MEMBER } from "../../variables";
|
||||||
} from '../../helpers/membership';
|
import { getSiteURL } from "../../config";
|
||||||
import { sendMail } from '../../helpers/nodemailer';
|
|
||||||
import { ADMIN, MEMBER, ACCEPTED } from '../../variables';
|
|
||||||
import { getSiteURL } from '../../config';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check that user is a member of workspace with id [workspaceId]
|
* Check that user is a member of workspace with id [workspaceId]
|
||||||
@@ -23,12 +20,12 @@ export const validateMembership = async (req: Request, res: Response) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!membership) {
|
if (!membership) {
|
||||||
throw new Error('Failed to validate membership');
|
throw new Error("Failed to validate membership");
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Workspace membership confirmed'
|
message: "Workspace membership confirmed"
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,12 +40,10 @@ export const deleteMembership = async (req: Request, res: Response) => {
|
|||||||
// check if membership to delete exists
|
// check if membership to delete exists
|
||||||
const membershipToDelete = await Membership.findOne({
|
const membershipToDelete = await Membership.findOne({
|
||||||
_id: membershipId
|
_id: membershipId
|
||||||
}).populate('user');
|
}).populate("user");
|
||||||
|
|
||||||
if (!membershipToDelete) {
|
if (!membershipToDelete) {
|
||||||
throw new Error(
|
throw new Error("Failed to delete workspace membership that doesn't exist");
|
||||||
"Failed to delete workspace membership that doesn't exist"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if user is a member and admin of the workspace
|
// check if user is a member and admin of the workspace
|
||||||
@@ -59,12 +54,12 @@ export const deleteMembership = async (req: Request, res: Response) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!membership) {
|
if (!membership) {
|
||||||
throw new Error('Failed to validate workspace membership');
|
throw new Error("Failed to validate workspace membership");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (membership.role !== ADMIN) {
|
if (membership.role !== ADMIN) {
|
||||||
// user is not an admin member of the workspace
|
// user is not an admin member of the workspace
|
||||||
throw new Error('Insufficient role for deleting workspace membership');
|
throw new Error("Insufficient role for deleting workspace membership");
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete workspace membership
|
// delete workspace membership
|
||||||
@@ -72,9 +67,9 @@ export const deleteMembership = async (req: Request, res: Response) => {
|
|||||||
membershipId: membershipToDelete._id.toString()
|
membershipId: membershipToDelete._id.toString()
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
deletedMembership
|
deletedMembership
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -88,7 +83,7 @@ export const changeMembershipRole = async (req: Request, res: Response) => {
|
|||||||
const { role } = req.body;
|
const { role } = req.body;
|
||||||
|
|
||||||
if (![ADMIN, MEMBER].includes(role)) {
|
if (![ADMIN, MEMBER].includes(role)) {
|
||||||
throw new Error('Failed to validate role');
|
throw new Error("Failed to validate role");
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate target membership
|
// validate target membership
|
||||||
@@ -97,7 +92,7 @@ export const changeMembershipRole = async (req: Request, res: Response) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!membershipToChangeRole) {
|
if (!membershipToChangeRole) {
|
||||||
throw new Error('Failed to find membership to change role');
|
throw new Error("Failed to find membership to change role");
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if user is a member and admin of target membership's
|
// check if user is a member and admin of target membership's
|
||||||
@@ -108,20 +103,20 @@ export const changeMembershipRole = async (req: Request, res: Response) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!membership) {
|
if (!membership) {
|
||||||
throw new Error('Failed to validate membership');
|
throw new Error("Failed to validate membership");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (membership.role !== ADMIN) {
|
if (membership.role !== ADMIN) {
|
||||||
// user is not an admin member of the workspace
|
// user is not an admin member of the workspace
|
||||||
throw new Error('Insufficient role for changing member roles');
|
throw new Error("Insufficient role for changing member roles");
|
||||||
}
|
}
|
||||||
|
|
||||||
membershipToChangeRole.role = role;
|
membershipToChangeRole.role = role;
|
||||||
await membershipToChangeRole.save();
|
await membershipToChangeRole.save();
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
membership: membershipToChangeRole
|
membership: membershipToChangeRole
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -136,10 +131,9 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
const invitee = await User.findOne({
|
const invitee = await User.findOne({
|
||||||
email
|
email
|
||||||
}).select('+publicKey');
|
}).select("+publicKey");
|
||||||
|
|
||||||
if (!invitee || !invitee?.publicKey)
|
if (!invitee || !invitee?.publicKey) throw new Error("Failed to validate invitee");
|
||||||
throw new Error('Failed to validate invitee');
|
|
||||||
|
|
||||||
// validate invitee's workspace membership - ensure member isn't
|
// validate invitee's workspace membership - ensure member isn't
|
||||||
// already a member of the workspace
|
// already a member of the workspace
|
||||||
@@ -148,8 +142,7 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => {
|
|||||||
workspace: workspaceId
|
workspace: workspaceId
|
||||||
});
|
});
|
||||||
|
|
||||||
if (inviteeMembership)
|
if (inviteeMembership) throw new Error("Failed to add existing member of workspace");
|
||||||
throw new Error('Failed to add existing member of workspace');
|
|
||||||
|
|
||||||
// validate invitee's organization membership - ensure that only
|
// validate invitee's organization membership - ensure that only
|
||||||
// (accepted) organization members can be added to the workspace
|
// (accepted) organization members can be added to the workspace
|
||||||
@@ -159,8 +152,7 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => {
|
|||||||
status: ACCEPTED
|
status: ACCEPTED
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!membershipOrg)
|
if (!membershipOrg) throw new Error("Failed to validate invitee's organization membership");
|
||||||
throw new Error("Failed to validate invitee's organization membership");
|
|
||||||
|
|
||||||
// get latest key
|
// get latest key
|
||||||
const latestKey = await Key.findOne({
|
const latestKey = await Key.findOne({
|
||||||
@@ -168,29 +160,29 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => {
|
|||||||
receiver: req.user._id
|
receiver: req.user._id
|
||||||
})
|
})
|
||||||
.sort({ createdAt: -1 })
|
.sort({ createdAt: -1 })
|
||||||
.populate('sender', '+publicKey');
|
.populate("sender", "+publicKey");
|
||||||
|
|
||||||
// create new workspace membership
|
// create new workspace membership
|
||||||
const m = await new Membership({
|
await new Membership({
|
||||||
user: invitee._id,
|
user: invitee._id,
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
role: MEMBER
|
role: MEMBER
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
await sendMail({
|
await sendMail({
|
||||||
template: 'workspaceInvitation.handlebars',
|
template: "workspaceInvitation.handlebars",
|
||||||
subjectLine: 'Infisical workspace invitation',
|
subjectLine: "Infisical workspace invitation",
|
||||||
recipients: [invitee.email],
|
recipients: [invitee.email],
|
||||||
substitutions: {
|
substitutions: {
|
||||||
inviterFirstName: req.user.firstName,
|
inviterFirstName: req.user.firstName,
|
||||||
inviterEmail: req.user.email,
|
inviterEmail: req.user.email,
|
||||||
workspaceName: req.membership.workspace.name,
|
workspaceName: req.membership.workspace.name,
|
||||||
callback_url: (await getSiteURL()) + '/login'
|
callback_url: (await getSiteURL()) + "/login"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
invitee,
|
invitee,
|
||||||
latestKey
|
latestKey
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,15 +1,27 @@
|
|||||||
import { Types } from 'mongoose';
|
import { Types } from "mongoose";
|
||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import { MembershipOrg, Organization, User } from '../../models';
|
import { MembershipOrg, Organization, User } from "../../models";
|
||||||
import { deleteMembershipOrg as deleteMemberFromOrg } from '../../helpers/membershipOrg';
|
import { deleteMembershipOrg as deleteMemberFromOrg } from "../../helpers/membershipOrg";
|
||||||
import { createToken } from '../../helpers/auth';
|
import { createToken } from "../../helpers/auth";
|
||||||
import { updateSubscriptionOrgQuantity } from '../../helpers/organization';
|
import { updateSubscriptionOrgQuantity } from "../../helpers/organization";
|
||||||
import { sendMail } from '../../helpers/nodemailer';
|
import { sendMail } from "../../helpers/nodemailer";
|
||||||
import { TokenService } from '../../services';
|
import { TokenService } from "../../services";
|
||||||
import { EELicenseService } from '../../ee/services';
|
import { EELicenseService } from "../../ee/services";
|
||||||
import { OWNER, ADMIN, MEMBER, ACCEPTED, INVITED, TOKEN_EMAIL_ORG_INVITATION } from '../../variables';
|
import {
|
||||||
import { getSiteURL, getJwtSignupLifetime, getJwtSignupSecret, getSmtpConfigured } from '../../config';
|
ACCEPTED,
|
||||||
import { validateUserEmail } from '../../validation';
|
ADMIN,
|
||||||
|
INVITED,
|
||||||
|
MEMBER,
|
||||||
|
OWNER,
|
||||||
|
TOKEN_EMAIL_ORG_INVITATION
|
||||||
|
} from "../../variables";
|
||||||
|
import {
|
||||||
|
getJwtSignupLifetime,
|
||||||
|
getJwtSignupSecret,
|
||||||
|
getSiteURL,
|
||||||
|
getSmtpConfigured
|
||||||
|
} from "../../config";
|
||||||
|
import { validateUserEmail } from "../../validation";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete organization membership with id [membershipOrgId] from organization
|
* Delete organization membership with id [membershipOrgId] from organization
|
||||||
@@ -17,18 +29,16 @@ import { validateUserEmail } from '../../validation';
|
|||||||
* @param res
|
* @param res
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const deleteMembershipOrg = async (req: Request, res: Response) => {
|
export const deleteMembershipOrg = async (req: Request, _res: Response) => {
|
||||||
const { membershipOrgId } = req.params;
|
const { membershipOrgId } = req.params;
|
||||||
|
|
||||||
// check if organization membership to delete exists
|
// check if organization membership to delete exists
|
||||||
const membershipOrgToDelete = await MembershipOrg.findOne({
|
const membershipOrgToDelete = await MembershipOrg.findOne({
|
||||||
_id: membershipOrgId
|
_id: membershipOrgId
|
||||||
}).populate('user');
|
}).populate("user");
|
||||||
|
|
||||||
if (!membershipOrgToDelete) {
|
if (!membershipOrgToDelete) {
|
||||||
throw new Error(
|
throw new Error("Failed to delete organization membership that doesn't exist");
|
||||||
"Failed to delete organization membership that doesn't exist"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if user is a member and admin of the organization
|
// check if user is a member and admin of the organization
|
||||||
@@ -39,16 +49,16 @@ export const deleteMembershipOrg = async (req: Request, res: Response) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!membershipOrg) {
|
if (!membershipOrg) {
|
||||||
throw new Error('Failed to validate organization membership');
|
throw new Error("Failed to validate organization membership");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (membershipOrg.role !== OWNER && membershipOrg.role !== ADMIN) {
|
if (membershipOrg.role !== OWNER && membershipOrg.role !== ADMIN) {
|
||||||
// user is not an admin member of the organization
|
// user is not an admin member of the organization
|
||||||
throw new Error('Insufficient role for deleting organization membership');
|
throw new Error("Insufficient role for deleting organization membership");
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete organization membership
|
// delete organization membership
|
||||||
const deletedMembershipOrg = await deleteMemberFromOrg({
|
await deleteMemberFromOrg({
|
||||||
membershipOrgId: membershipOrgToDelete._id.toString()
|
membershipOrgId: membershipOrgToDelete._id.toString()
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -56,7 +66,7 @@ export const deleteMembershipOrg = async (req: Request, res: Response) => {
|
|||||||
organizationId: membershipOrg.organization.toString()
|
organizationId: membershipOrg.organization.toString()
|
||||||
});
|
});
|
||||||
|
|
||||||
return membershipOrgToDelete;
|
return membershipOrgToDelete;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,14 +76,14 @@ export const deleteMembershipOrg = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const changeMembershipOrgRole = async (req: Request, res: Response) => {
|
export const changeMembershipOrgRole = async (req: Request, res: Response) => {
|
||||||
// change role for (target) organization membership with id
|
// change role for (target) organization membership with id
|
||||||
// [membershipOrgId]
|
// [membershipOrgId]
|
||||||
|
|
||||||
let membershipToChangeRole;
|
let membershipToChangeRole;
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
membershipOrg: membershipToChangeRole
|
membershipOrg: membershipToChangeRole
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,7 +94,7 @@ export const changeMembershipOrgRole = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const inviteUserToOrganization = async (req: Request, res: Response) => {
|
export const inviteUserToOrganization = async (req: Request, res: Response) => {
|
||||||
let invitee, inviteeMembershipOrg, completeInviteLink;
|
let inviteeMembershipOrg, completeInviteLink;
|
||||||
const { organizationId, inviteeEmail } = req.body;
|
const { organizationId, inviteeEmail } = req.body;
|
||||||
const host = req.headers.host;
|
const host = req.headers.host;
|
||||||
const siteUrl = `${req.protocol}://${host}`;
|
const siteUrl = `${req.protocol}://${host}`;
|
||||||
@@ -96,25 +106,26 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!membershipOrg) {
|
if (!membershipOrg) {
|
||||||
throw new Error('Failed to validate organization membership');
|
throw new Error("Failed to validate organization membership");
|
||||||
}
|
}
|
||||||
|
|
||||||
const plan = await EELicenseService.getPlan(organizationId);
|
const plan = await EELicenseService.getPlan(organizationId);
|
||||||
|
|
||||||
if (plan.memberLimit !== null) {
|
if (plan.memberLimit !== null) {
|
||||||
// case: limit imposed on number of members allowed
|
// case: limit imposed on number of members allowed
|
||||||
|
|
||||||
if (plan.membersUsed >= plan.memberLimit) {
|
if (plan.membersUsed >= plan.memberLimit) {
|
||||||
// case: number of members used exceeds the number of members allowed
|
// case: number of members used exceeds the number of members allowed
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
message: 'Failed to invite member due to member limit reached. Upgrade plan to invite more members.'
|
message:
|
||||||
|
"Failed to invite member due to member limit reached. Upgrade plan to invite more members."
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
invitee = await User.findOne({
|
const invitee = await User.findOne({
|
||||||
email: inviteeEmail
|
email: inviteeEmail
|
||||||
}).select('+publicKey');
|
}).select("+publicKey");
|
||||||
|
|
||||||
if (invitee) {
|
if (invitee) {
|
||||||
// case: invitee is an existing user
|
// case: invitee is an existing user
|
||||||
@@ -125,13 +136,10 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (inviteeMembershipOrg && inviteeMembershipOrg.status === ACCEPTED) {
|
if (inviteeMembershipOrg && inviteeMembershipOrg.status === ACCEPTED) {
|
||||||
throw new Error(
|
throw new Error("Failed to invite an existing member of the organization");
|
||||||
'Failed to invite an existing member of the organization'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!inviteeMembershipOrg) {
|
if (!inviteeMembershipOrg) {
|
||||||
|
|
||||||
await new MembershipOrg({
|
await new MembershipOrg({
|
||||||
user: invitee,
|
user: invitee,
|
||||||
inviteEmail: inviteeEmail,
|
inviteEmail: inviteeEmail,
|
||||||
@@ -149,7 +157,7 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
if (!inviteeMembershipOrg) {
|
if (!inviteeMembershipOrg) {
|
||||||
// case: invitee has never been invited before
|
// case: invitee has never been invited before
|
||||||
|
|
||||||
// validate that email is not disposable
|
// validate that email is not disposable
|
||||||
validateUserEmail(inviteeEmail);
|
validateUserEmail(inviteeEmail);
|
||||||
|
|
||||||
@@ -165,7 +173,6 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
|
|||||||
const organization = await Organization.findOne({ _id: organizationId });
|
const organization = await Organization.findOne({ _id: organizationId });
|
||||||
|
|
||||||
if (organization) {
|
if (organization) {
|
||||||
|
|
||||||
const token = await TokenService.createToken({
|
const token = await TokenService.createToken({
|
||||||
type: TOKEN_EMAIL_ORG_INVITATION,
|
type: TOKEN_EMAIL_ORG_INVITATION,
|
||||||
email: inviteeEmail,
|
email: inviteeEmail,
|
||||||
@@ -173,8 +180,8 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await sendMail({
|
await sendMail({
|
||||||
template: 'organizationInvitation.handlebars',
|
template: "organizationInvitation.handlebars",
|
||||||
subjectLine: 'Infisical organization invitation',
|
subjectLine: "Infisical organization invitation",
|
||||||
recipients: [inviteeEmail],
|
recipients: [inviteeEmail],
|
||||||
substitutions: {
|
substitutions: {
|
||||||
inviterFirstName: req.user.firstName,
|
inviterFirstName: req.user.firstName,
|
||||||
@@ -183,21 +190,23 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
|
|||||||
email: inviteeEmail,
|
email: inviteeEmail,
|
||||||
organizationId: organization._id.toString(),
|
organizationId: organization._id.toString(),
|
||||||
token,
|
token,
|
||||||
callback_url: (await getSiteURL()) + '/signupinvite'
|
callback_url: (await getSiteURL()) + "/signupinvite"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!(await getSmtpConfigured())) {
|
if (!(await getSmtpConfigured())) {
|
||||||
completeInviteLink = `${siteUrl + '/signupinvite'}?token=${token}&to=${inviteeEmail}&organization_id=${organization._id}`
|
completeInviteLink = `${
|
||||||
|
siteUrl + "/signupinvite"
|
||||||
|
}?token=${token}&to=${inviteeEmail}&organization_id=${organization._id}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await updateSubscriptionOrgQuantity({ organizationId });
|
await updateSubscriptionOrgQuantity({ organizationId });
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: `Sent an invite link to ${req.body.inviteeEmail}`,
|
message: `Sent an invite link to ${req.body.inviteeEmail}`,
|
||||||
completeInviteLink
|
completeInviteLink
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -208,14 +217,10 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const verifyUserToOrganization = async (req: Request, res: Response) => {
|
export const verifyUserToOrganization = async (req: Request, res: Response) => {
|
||||||
let user;
|
let user;
|
||||||
const {
|
const { email, organizationId, code } = req.body;
|
||||||
email,
|
|
||||||
organizationId,
|
|
||||||
code
|
|
||||||
} = req.body;
|
|
||||||
|
|
||||||
user = await User.findOne({ email }).select('+publicKey');
|
user = await User.findOne({ email }).select("+publicKey");
|
||||||
|
|
||||||
const membershipOrg = await MembershipOrg.findOne({
|
const membershipOrg = await MembershipOrg.findOne({
|
||||||
inviteEmail: email,
|
inviteEmail: email,
|
||||||
@@ -223,8 +228,7 @@ export const verifyUserToOrganization = async (req: Request, res: Response) => {
|
|||||||
organization: new Types.ObjectId(organizationId)
|
organization: new Types.ObjectId(organizationId)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!membershipOrg)
|
if (!membershipOrg) throw new Error("Failed to find any invitations for email");
|
||||||
throw new Error('Failed to find any invitations for email');
|
|
||||||
|
|
||||||
await TokenService.validateToken({
|
await TokenService.validateToken({
|
||||||
type: TOKEN_EMAIL_ORG_INVITATION,
|
type: TOKEN_EMAIL_ORG_INVITATION,
|
||||||
@@ -238,14 +242,14 @@ export const verifyUserToOrganization = async (req: Request, res: Response) => {
|
|||||||
// membership can be approved and redirected to login/dashboard
|
// membership can be approved and redirected to login/dashboard
|
||||||
membershipOrg.status = ACCEPTED;
|
membershipOrg.status = ACCEPTED;
|
||||||
await membershipOrg.save();
|
await membershipOrg.save();
|
||||||
|
|
||||||
await updateSubscriptionOrgQuantity({
|
await updateSubscriptionOrgQuantity({
|
||||||
organizationId
|
organizationId
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfully verified email',
|
message: "Successfully verified email",
|
||||||
user,
|
user
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,9 +269,9 @@ export const verifyUserToOrganization = async (req: Request, res: Response) => {
|
|||||||
secret: await getJwtSignupSecret()
|
secret: await getJwtSignupSecret()
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfully verified email',
|
message: "Successfully verified email",
|
||||||
user,
|
user,
|
||||||
token
|
token
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,28 +1,27 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import Stripe from 'stripe';
|
import Stripe from "stripe";
|
||||||
import {
|
import {
|
||||||
|
IncidentContactOrg,
|
||||||
Membership,
|
Membership,
|
||||||
MembershipOrg,
|
MembershipOrg,
|
||||||
Organization,
|
Organization,
|
||||||
Workspace,
|
Workspace,
|
||||||
IncidentContactOrg
|
} from "../../models";
|
||||||
} from '../../models';
|
import { createOrganization as create } from "../../helpers/organization";
|
||||||
import { createOrganization as create } from '../../helpers/organization';
|
import { addMembershipsOrg } from "../../helpers/membershipOrg";
|
||||||
import { addMembershipsOrg } from '../../helpers/membershipOrg';
|
import { ACCEPTED, OWNER } from "../../variables";
|
||||||
import { OWNER, ACCEPTED } from '../../variables';
|
import { getSiteURL, getStripeSecretKey } from "../../config";
|
||||||
import _ from 'lodash';
|
|
||||||
import { getStripeSecretKey, getSiteURL } from '../../config';
|
|
||||||
|
|
||||||
export const getOrganizations = async (req: Request, res: Response) => {
|
export const getOrganizations = async (req: Request, res: Response) => {
|
||||||
const organizations = (
|
const organizations = (
|
||||||
await MembershipOrg.find({
|
await MembershipOrg.find({
|
||||||
user: req.user._id,
|
user: req.user._id,
|
||||||
status: ACCEPTED
|
status: ACCEPTED,
|
||||||
}).populate('organization')
|
}).populate("organization")
|
||||||
).map((m) => m.organization);
|
).map((m) => m.organization);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
organizations
|
organizations,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -37,24 +36,24 @@ export const createOrganization = async (req: Request, res: Response) => {
|
|||||||
const { organizationName } = req.body;
|
const { organizationName } = req.body;
|
||||||
|
|
||||||
if (organizationName.length < 1) {
|
if (organizationName.length < 1) {
|
||||||
throw new Error('Organization names must be at least 1-character long');
|
throw new Error("Organization names must be at least 1-character long");
|
||||||
}
|
}
|
||||||
|
|
||||||
// create organization and add user as member
|
// create organization and add user as member
|
||||||
const organization = await create({
|
const organization = await create({
|
||||||
email: req.user.email,
|
email: req.user.email,
|
||||||
name: organizationName
|
name: organizationName,
|
||||||
});
|
});
|
||||||
|
|
||||||
await addMembershipsOrg({
|
await addMembershipsOrg({
|
||||||
userIds: [req.user._id.toString()],
|
userIds: [req.user._id.toString()],
|
||||||
organizationId: organization._id.toString(),
|
organizationId: organization._id.toString(),
|
||||||
roles: [OWNER],
|
roles: [OWNER],
|
||||||
statuses: [ACCEPTED]
|
statuses: [ACCEPTED],
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
organization
|
organization,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -67,7 +66,7 @@ export const createOrganization = async (req: Request, res: Response) => {
|
|||||||
export const getOrganization = async (req: Request, res: Response) => {
|
export const getOrganization = async (req: Request, res: Response) => {
|
||||||
const organization = req.organization
|
const organization = req.organization
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
organization
|
organization,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -81,11 +80,11 @@ export const getOrganizationMembers = async (req: Request, res: Response) => {
|
|||||||
const { organizationId } = req.params;
|
const { organizationId } = req.params;
|
||||||
|
|
||||||
const users = await MembershipOrg.find({
|
const users = await MembershipOrg.find({
|
||||||
organization: organizationId
|
organization: organizationId,
|
||||||
}).populate('user', '+publicKey');
|
}).populate("user", "+publicKey");
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
users
|
users,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -105,23 +104,23 @@ export const getOrganizationWorkspaces = async (
|
|||||||
(
|
(
|
||||||
await Workspace.find(
|
await Workspace.find(
|
||||||
{
|
{
|
||||||
organization: organizationId
|
organization: organizationId,
|
||||||
},
|
},
|
||||||
'_id'
|
"_id"
|
||||||
)
|
)
|
||||||
).map((w) => w._id.toString())
|
).map((w) => w._id.toString())
|
||||||
);
|
);
|
||||||
|
|
||||||
const workspaces = (
|
const workspaces = (
|
||||||
await Membership.find({
|
await Membership.find({
|
||||||
user: req.user._id
|
user: req.user._id,
|
||||||
}).populate('workspace')
|
}).populate("workspace")
|
||||||
)
|
)
|
||||||
.filter((m) => workspacesSet.has(m.workspace._id.toString()))
|
.filter((m) => workspacesSet.has(m.workspace._id.toString()))
|
||||||
.map((m) => m.workspace);
|
.map((m) => m.workspace);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
workspaces
|
workspaces,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -137,19 +136,19 @@ export const changeOrganizationName = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
const organization = await Organization.findOneAndUpdate(
|
const organization = await Organization.findOneAndUpdate(
|
||||||
{
|
{
|
||||||
_id: organizationId
|
_id: organizationId,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name
|
name,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
new: true
|
new: true,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfully changed organization name',
|
message: "Successfully changed organization name",
|
||||||
organization
|
organization,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -166,11 +165,11 @@ export const getOrganizationIncidentContacts = async (
|
|||||||
const { organizationId } = req.params;
|
const { organizationId } = req.params;
|
||||||
|
|
||||||
const incidentContactsOrg = await IncidentContactOrg.find({
|
const incidentContactsOrg = await IncidentContactOrg.find({
|
||||||
organization: organizationId
|
organization: organizationId,
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
incidentContactsOrg
|
incidentContactsOrg,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -194,7 +193,7 @@ export const addOrganizationIncidentContact = async (
|
|||||||
);
|
);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
incidentContactOrg
|
incidentContactOrg,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -213,12 +212,12 @@ export const deleteOrganizationIncidentContact = async (
|
|||||||
|
|
||||||
const incidentContactOrg = await IncidentContactOrg.findOneAndDelete({
|
const incidentContactOrg = await IncidentContactOrg.findOneAndDelete({
|
||||||
email,
|
email,
|
||||||
organization: organizationId
|
organization: organizationId,
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfully deleted organization incident contact',
|
message: "Successfully deleted organization incident contact",
|
||||||
incidentContactOrg
|
incidentContactOrg,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -235,28 +234,28 @@ export const createOrganizationPortalSession = async (
|
|||||||
) => {
|
) => {
|
||||||
let session;
|
let session;
|
||||||
const stripe = new Stripe(await getStripeSecretKey(), {
|
const stripe = new Stripe(await getStripeSecretKey(), {
|
||||||
apiVersion: '2022-08-01'
|
apiVersion: "2022-08-01",
|
||||||
});
|
});
|
||||||
|
|
||||||
// check if there is a payment method on file
|
// check if there is a payment method on file
|
||||||
const paymentMethods = await stripe.paymentMethods.list({
|
const paymentMethods = await stripe.paymentMethods.list({
|
||||||
customer: req.organization.customerId,
|
customer: req.organization.customerId,
|
||||||
type: 'card'
|
type: "card",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (paymentMethods.data.length < 1) {
|
if (paymentMethods.data.length < 1) {
|
||||||
// case: no payment method on file
|
// case: no payment method on file
|
||||||
session = await stripe.checkout.sessions.create({
|
session = await stripe.checkout.sessions.create({
|
||||||
customer: req.organization.customerId,
|
customer: req.organization.customerId,
|
||||||
mode: 'setup',
|
mode: "setup",
|
||||||
payment_method_types: ['card'],
|
payment_method_types: ["card"],
|
||||||
success_url: (await getSiteURL()) + '/dashboard',
|
success_url: (await getSiteURL()) + "/dashboard",
|
||||||
cancel_url: (await getSiteURL()) + '/dashboard'
|
cancel_url: (await getSiteURL()) + "/dashboard",
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
session = await stripe.billingPortal.sessions.create({
|
session = await stripe.billingPortal.sessions.create({
|
||||||
customer: req.organization.customerId,
|
customer: req.organization.customerId,
|
||||||
return_url: (await getSiteURL()) + '/dashboard'
|
return_url: (await getSiteURL()) + "/dashboard",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,15 +273,15 @@ export const getOrganizationSubscriptions = async (
|
|||||||
res: Response
|
res: Response
|
||||||
) => {
|
) => {
|
||||||
const stripe = new Stripe(await getStripeSecretKey(), {
|
const stripe = new Stripe(await getStripeSecretKey(), {
|
||||||
apiVersion: '2022-08-01'
|
apiVersion: "2022-08-01",
|
||||||
});
|
});
|
||||||
|
|
||||||
const subscriptions = await stripe.subscriptions.list({
|
const subscriptions = await stripe.subscriptions.list({
|
||||||
customer: req.organization.customerId
|
customer: req.organization.customerId,
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
subscriptions
|
subscriptions,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -302,16 +301,16 @@ export const getOrganizationMembersAndTheirWorkspaces = async (
|
|||||||
const workspacesSet = (
|
const workspacesSet = (
|
||||||
await Workspace.find(
|
await Workspace.find(
|
||||||
{
|
{
|
||||||
organization: organizationId
|
organization: organizationId,
|
||||||
},
|
},
|
||||||
'_id'
|
"_id"
|
||||||
)
|
)
|
||||||
).map((w) => w._id.toString());
|
).map((w) => w._id.toString());
|
||||||
|
|
||||||
const memberships = (
|
const memberships = (
|
||||||
await Membership.find({
|
await Membership.find({
|
||||||
workspace: { $in: workspacesSet }
|
workspace: { $in: workspacesSet },
|
||||||
}).populate('workspace')
|
}).populate("workspace")
|
||||||
);
|
);
|
||||||
const userToWorkspaceIds: any = {};
|
const userToWorkspaceIds: any = {};
|
||||||
|
|
||||||
|
|||||||
@@ -1,85 +1,77 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const jsrp = require('jsrp');
|
const jsrp = require("jsrp");
|
||||||
import * as bigintConversion from 'bigint-conversion';
|
import * as bigintConversion from "bigint-conversion";
|
||||||
import { User, BackupPrivateKey, LoginSRPDetail } from '../../models';
|
import { BackupPrivateKey, LoginSRPDetail, User } from "../../models";
|
||||||
|
import { clearTokens, createToken, sendMail } from "../../helpers";
|
||||||
|
import { TokenService } from "../../services";
|
||||||
|
import { AUTH_MODE_JWT, TOKEN_EMAIL_PASSWORD_RESET } from "../../variables";
|
||||||
|
import { BadRequestError } from "../../utils/errors";
|
||||||
import {
|
import {
|
||||||
createToken,
|
getHttpsEnabled,
|
||||||
sendMail,
|
getJwtSignupLifetime,
|
||||||
clearTokens
|
getJwtSignupSecret,
|
||||||
} from '../../helpers';
|
getSiteURL
|
||||||
import { TokenService } from '../../services';
|
} from "../../config";
|
||||||
import {
|
|
||||||
TOKEN_EMAIL_PASSWORD_RESET,
|
|
||||||
AUTH_MODE_JWT
|
|
||||||
} from '../../variables';
|
|
||||||
import { BadRequestError } from '../../utils/errors';
|
|
||||||
import {
|
|
||||||
getSiteURL,
|
|
||||||
getJwtSignupLifetime,
|
|
||||||
getJwtSignupSecret,
|
|
||||||
getHttpsEnabled
|
|
||||||
} from '../../config';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Password reset step 1: Send email verification link to email [email]
|
* Password reset step 1: Send email verification link to email [email]
|
||||||
* for account recovery.
|
* for account recovery.
|
||||||
* @param req
|
* @param req
|
||||||
* @param res
|
* @param res
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const emailPasswordReset = async (req: Request, res: Response) => {
|
export const emailPasswordReset = async (req: Request, res: Response) => {
|
||||||
let email: string;
|
const email: string = req.body.email;
|
||||||
email = req.body.email;
|
|
||||||
|
|
||||||
const user = await User.findOne({ email }).select('+publicKey');
|
const user = await User.findOne({ email }).select("+publicKey");
|
||||||
if (!user || !user?.publicKey) {
|
if (!user || !user?.publicKey) {
|
||||||
// case: user has already completed account
|
// case: user has already completed account
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message:"If an account exists with this email, a password reset link has been sent"
|
message: "If an account exists with this email, a password reset link has been sent"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = await TokenService.createToken({
|
const token = await TokenService.createToken({
|
||||||
type: TOKEN_EMAIL_PASSWORD_RESET,
|
type: TOKEN_EMAIL_PASSWORD_RESET,
|
||||||
email
|
email
|
||||||
});
|
});
|
||||||
|
|
||||||
await sendMail({
|
await sendMail({
|
||||||
template: 'passwordReset.handlebars',
|
template: "passwordReset.handlebars",
|
||||||
subjectLine: 'Infisical password reset',
|
subjectLine: "Infisical password reset",
|
||||||
recipients: [email],
|
recipients: [email],
|
||||||
substitutions: {
|
substitutions: {
|
||||||
email,
|
email,
|
||||||
token,
|
token,
|
||||||
callback_url: (await getSiteURL()) + '/password-reset'
|
callback_url: (await getSiteURL()) + "/password-reset"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message:"If an account exists with this email, a password reset link has been sent"
|
message: "If an account exists with this email, a password reset link has been sent"
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Password reset step 2: Verify email verification link sent to email [email]
|
* Password reset step 2: Verify email verification link sent to email [email]
|
||||||
* @param req
|
* @param req
|
||||||
* @param res
|
* @param res
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const emailPasswordResetVerify = async (req: Request, res: Response) => {
|
export const emailPasswordResetVerify = async (req: Request, res: Response) => {
|
||||||
const { email, code } = req.body;
|
const { email, code } = req.body;
|
||||||
|
|
||||||
const user = await User.findOne({ email }).select('+publicKey');
|
const user = await User.findOne({ email }).select("+publicKey");
|
||||||
if (!user || !user?.publicKey) {
|
if (!user || !user?.publicKey) {
|
||||||
// case: user doesn't exist with email [email] or
|
// case: user doesn't exist with email [email] or
|
||||||
// hasn't even completed their account
|
// hasn't even completed their account
|
||||||
return res.status(403).send({
|
return res.status(403).send({
|
||||||
error: 'Failed email verification for password reset'
|
error: "Failed email verification for password reset"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await TokenService.validateToken({
|
await TokenService.validateToken({
|
||||||
type: TOKEN_EMAIL_PASSWORD_RESET,
|
type: TOKEN_EMAIL_PASSWORD_RESET,
|
||||||
email,
|
email,
|
||||||
@@ -95,12 +87,12 @@ export const emailPasswordResetVerify = async (req: Request, res: Response) => {
|
|||||||
secret: await getJwtSignupSecret()
|
secret: await getJwtSignupSecret()
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfully verified email',
|
message: "Successfully verified email",
|
||||||
user,
|
user,
|
||||||
token
|
token
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return [salt] and [serverPublicKey] as part of step 1 of SRP protocol
|
* Return [salt] and [serverPublicKey] as part of step 1 of SRP protocol
|
||||||
@@ -109,14 +101,14 @@ export const emailPasswordResetVerify = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const srp1 = async (req: Request, res: Response) => {
|
export const srp1 = async (req: Request, res: Response) => {
|
||||||
// return salt, serverPublicKey as part of first step of SRP protocol
|
// return salt, serverPublicKey as part of first step of SRP protocol
|
||||||
|
|
||||||
const { clientPublicKey } = req.body;
|
const { clientPublicKey } = req.body;
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
email: req.user.email
|
email: req.user.email
|
||||||
}).select('+salt +verifier');
|
}).select("+salt +verifier");
|
||||||
|
|
||||||
if (!user) throw new Error('Failed to find user');
|
if (!user) throw new Error("Failed to find user");
|
||||||
|
|
||||||
const server = new jsrp.server();
|
const server = new jsrp.server();
|
||||||
server.init(
|
server.init(
|
||||||
@@ -128,11 +120,15 @@ export const srp1 = async (req: Request, res: Response) => {
|
|||||||
// generate server-side public key
|
// generate server-side public key
|
||||||
const serverPublicKey = server.getPublicKey();
|
const serverPublicKey = server.getPublicKey();
|
||||||
|
|
||||||
await LoginSRPDetail.findOneAndReplace({ email: req.user.email }, {
|
await LoginSRPDetail.findOneAndReplace(
|
||||||
email: req.user.email,
|
{ email: req.user.email },
|
||||||
clientPublicKey: clientPublicKey,
|
{
|
||||||
serverBInt: bigintConversion.bigintToBuf(server.bInt),
|
email: req.user.email,
|
||||||
}, { upsert: true, returnNewDocument: false })
|
clientPublicKey: clientPublicKey,
|
||||||
|
serverBInt: bigintConversion.bigintToBuf(server.bInt)
|
||||||
|
},
|
||||||
|
{ upsert: true, returnNewDocument: false }
|
||||||
|
);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
serverPublicKey,
|
serverPublicKey,
|
||||||
@@ -140,8 +136,7 @@ export const srp1 = async (req: Request, res: Response) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change account SRP authentication information for user
|
* Change account SRP authentication information for user
|
||||||
@@ -152,8 +147,8 @@ export const srp1 = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const changePassword = async (req: Request, res: Response) => {
|
export const changePassword = async (req: Request, res: Response) => {
|
||||||
const {
|
const {
|
||||||
clientProof,
|
clientProof,
|
||||||
protectedKey,
|
protectedKey,
|
||||||
protectedKeyIV,
|
protectedKeyIV,
|
||||||
protectedKeyTag,
|
protectedKeyTag,
|
||||||
@@ -166,14 +161,18 @@ export const changePassword = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
email: req.user.email
|
email: req.user.email
|
||||||
}).select('+salt +verifier');
|
}).select("+salt +verifier");
|
||||||
|
|
||||||
if (!user) throw new Error('Failed to find user');
|
if (!user) throw new Error("Failed to find user");
|
||||||
|
|
||||||
const loginSRPDetailFromDB = await LoginSRPDetail.findOneAndDelete({ email: req.user.email })
|
const loginSRPDetailFromDB = await LoginSRPDetail.findOneAndDelete({ email: req.user.email });
|
||||||
|
|
||||||
if (!loginSRPDetailFromDB) {
|
if (!loginSRPDetailFromDB) {
|
||||||
return BadRequestError(Error("It looks like some details from the first login are not found. Please try login one again"))
|
return BadRequestError(
|
||||||
|
Error(
|
||||||
|
"It looks like some details from the first login are not found. Please try login one again"
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const server = new jsrp.server();
|
const server = new jsrp.server();
|
||||||
@@ -207,27 +206,31 @@ export const changePassword = async (req: Request, res: Response) => {
|
|||||||
new: true
|
new: true
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (req.authData.authMode === AUTH_MODE_JWT && req.authData.authPayload instanceof User && req.authData.tokenVersionId) {
|
if (
|
||||||
await clearTokens(req.authData.tokenVersionId)
|
req.authData.authMode === AUTH_MODE_JWT &&
|
||||||
|
req.authData.authPayload instanceof User &&
|
||||||
|
req.authData.tokenVersionId
|
||||||
|
) {
|
||||||
|
await clearTokens(req.authData.tokenVersionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear httpOnly cookie
|
// clear httpOnly cookie
|
||||||
|
|
||||||
res.cookie('jid', '', {
|
res.cookie("jid", "", {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
path: '/',
|
path: "/",
|
||||||
sameSite: 'strict',
|
sameSite: "strict",
|
||||||
secure: (await getHttpsEnabled()) as boolean
|
secure: (await getHttpsEnabled()) as boolean
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfully changed password'
|
message: "Successfully changed password"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
error: 'Failed to change password. Try again?'
|
error: "Failed to change password. Try again?"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -240,22 +243,25 @@ export const changePassword = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const createBackupPrivateKey = async (req: Request, res: Response) => {
|
export const createBackupPrivateKey = async (req: Request, res: Response) => {
|
||||||
// create/change backup private key
|
// create/change backup private key
|
||||||
// requires verifying [clientProof] as part of second step of SRP protocol
|
// requires verifying [clientProof] as part of second step of SRP protocol
|
||||||
// as initiated in /srp1
|
// as initiated in /srp1
|
||||||
|
|
||||||
const { clientProof, encryptedPrivateKey, iv, tag, salt, verifier } =
|
const { clientProof, encryptedPrivateKey, iv, tag, salt, verifier } = req.body;
|
||||||
req.body;
|
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
email: req.user.email
|
email: req.user.email
|
||||||
}).select('+salt +verifier');
|
}).select("+salt +verifier");
|
||||||
|
|
||||||
if (!user) throw new Error('Failed to find user');
|
if (!user) throw new Error("Failed to find user");
|
||||||
|
|
||||||
const loginSRPDetailFromDB = await LoginSRPDetail.findOneAndDelete({ email: req.user.email })
|
const loginSRPDetailFromDB = await LoginSRPDetail.findOneAndDelete({ email: req.user.email });
|
||||||
|
|
||||||
if (!loginSRPDetailFromDB) {
|
if (!loginSRPDetailFromDB) {
|
||||||
return BadRequestError(Error("It looks like some details from the first login are not found. Please try login one again"))
|
return BadRequestError(
|
||||||
|
Error(
|
||||||
|
"It looks like some details from the first login are not found. Please try login one again"
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const server = new jsrp.server();
|
const server = new jsrp.server();
|
||||||
@@ -266,9 +272,7 @@ export const createBackupPrivateKey = async (req: Request, res: Response) => {
|
|||||||
b: loginSRPDetailFromDB.serverBInt
|
b: loginSRPDetailFromDB.serverBInt
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
server.setClientPublicKey(
|
server.setClientPublicKey(loginSRPDetailFromDB.clientPublicKey);
|
||||||
loginSRPDetailFromDB.clientPublicKey
|
|
||||||
);
|
|
||||||
|
|
||||||
// compare server and client shared keys
|
// compare server and client shared keys
|
||||||
if (server.checkClientProof(clientProof)) {
|
if (server.checkClientProof(clientProof)) {
|
||||||
@@ -285,17 +289,17 @@ export const createBackupPrivateKey = async (req: Request, res: Response) => {
|
|||||||
verifier
|
verifier
|
||||||
},
|
},
|
||||||
{ upsert: true, new: true }
|
{ upsert: true, new: true }
|
||||||
).select('+user, encryptedPrivateKey');
|
).select("+user, encryptedPrivateKey");
|
||||||
|
|
||||||
// issue tokens
|
// issue tokens
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfully updated backup private key',
|
message: "Successfully updated backup private key",
|
||||||
backupPrivateKey
|
backupPrivateKey
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
message: 'Failed to update backup private key'
|
message: "Failed to update backup private key"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -303,21 +307,21 @@ export const createBackupPrivateKey = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Return backup private key for user
|
* Return backup private key for user
|
||||||
* @param req
|
* @param req
|
||||||
* @param res
|
* @param res
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getBackupPrivateKey = async (req: Request, res: Response) => {
|
export const getBackupPrivateKey = async (req: Request, res: Response) => {
|
||||||
const backupPrivateKey = await BackupPrivateKey.findOne({
|
const backupPrivateKey = await BackupPrivateKey.findOne({
|
||||||
user: req.user._id
|
user: req.user._id
|
||||||
}).select('+encryptedPrivateKey +iv +tag');
|
}).select("+encryptedPrivateKey +iv +tag");
|
||||||
|
|
||||||
if (!backupPrivateKey) throw new Error('Failed to find backup private key');
|
if (!backupPrivateKey) throw new Error("Failed to find backup private key");
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
backupPrivateKey
|
backupPrivateKey
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
export const resetPassword = async (req: Request, res: Response) => {
|
export const resetPassword = async (req: Request, res: Response) => {
|
||||||
const {
|
const {
|
||||||
@@ -328,7 +332,7 @@ export const resetPassword = async (req: Request, res: Response) => {
|
|||||||
encryptedPrivateKeyIV,
|
encryptedPrivateKeyIV,
|
||||||
encryptedPrivateKeyTag,
|
encryptedPrivateKeyTag,
|
||||||
salt,
|
salt,
|
||||||
verifier,
|
verifier
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
await User.findByIdAndUpdate(
|
await User.findByIdAndUpdate(
|
||||||
@@ -337,7 +341,7 @@ export const resetPassword = async (req: Request, res: Response) => {
|
|||||||
encryptionVersion: 2,
|
encryptionVersion: 2,
|
||||||
protectedKey,
|
protectedKey,
|
||||||
protectedKeyIV,
|
protectedKeyIV,
|
||||||
protectedKeyTag,
|
protectedKeyTag,
|
||||||
encryptedPrivateKey,
|
encryptedPrivateKey,
|
||||||
iv: encryptedPrivateKeyIV,
|
iv: encryptedPrivateKeyIV,
|
||||||
tag: encryptedPrivateKeyTag,
|
tag: encryptedPrivateKeyTag,
|
||||||
@@ -349,7 +353,7 @@ export const resetPassword = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfully reset password'
|
message: "Successfully reset password"
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,30 +1,30 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import { Types } from 'mongoose';
|
import { Types } from "mongoose";
|
||||||
import { Key, Secret } from '../../models';
|
import { Key } from "../../models";
|
||||||
import {
|
import {
|
||||||
v1PushSecrets as push,
|
pullSecrets as pull,
|
||||||
pullSecrets as pull,
|
v1PushSecrets as push,
|
||||||
reformatPullSecrets
|
reformatPullSecrets
|
||||||
} from '../../helpers/secret';
|
} from "../../helpers/secret";
|
||||||
import { pushKeys } from '../../helpers/key';
|
import { pushKeys } from "../../helpers/key";
|
||||||
import { eventPushSecrets } from '../../events';
|
import { eventPushSecrets } from "../../events";
|
||||||
import { EventService } from '../../services';
|
import { EventService } from "../../services";
|
||||||
import { TelemetryService } from '../../services';
|
import { TelemetryService } from "../../services";
|
||||||
|
|
||||||
interface PushSecret {
|
interface PushSecret {
|
||||||
ciphertextKey: string;
|
ciphertextKey: string;
|
||||||
ivKey: string;
|
ivKey: string;
|
||||||
tagKey: string;
|
tagKey: string;
|
||||||
hashKey: string;
|
hashKey: string;
|
||||||
ciphertextValue: string;
|
ciphertextValue: string;
|
||||||
ivValue: string;
|
ivValue: string;
|
||||||
tagValue: string;
|
tagValue: string;
|
||||||
hashValue: string;
|
hashValue: string;
|
||||||
ciphertextComment: string;
|
ciphertextComment: string;
|
||||||
ivComment: string;
|
ivComment: string;
|
||||||
tagComment: string;
|
tagComment: string;
|
||||||
hashComment: string;
|
hashComment: string;
|
||||||
type: 'shared' | 'personal';
|
type: "shared" | "personal";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,7 +35,7 @@ interface PushSecret {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const pushSecrets = async (req: Request, res: Response) => {
|
export const pushSecrets = async (req: Request, res: Response) => {
|
||||||
// upload (encrypted) secrets to workspace with id [workspaceId]
|
// upload (encrypted) secrets to workspace with id [workspaceId]
|
||||||
const postHogClient = await TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
let { secrets }: { secrets: PushSecret[] } = req.body;
|
let { secrets }: { secrets: PushSecret[] } = req.body;
|
||||||
const { keys, environment, channel } = req.body;
|
const { keys, environment, channel } = req.body;
|
||||||
@@ -44,13 +44,11 @@ export const pushSecrets = async (req: Request, res: Response) => {
|
|||||||
// validate environment
|
// validate environment
|
||||||
const workspaceEnvs = req.membership.workspace.environments;
|
const workspaceEnvs = req.membership.workspace.environments;
|
||||||
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
|
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
|
||||||
throw new Error('Failed to validate environment');
|
throw new Error("Failed to validate environment");
|
||||||
}
|
}
|
||||||
|
|
||||||
// sanitize secrets
|
// sanitize secrets
|
||||||
secrets = secrets.filter(
|
secrets = secrets.filter((s: PushSecret) => s.ciphertextKey !== "" && s.ciphertextValue !== "");
|
||||||
(s: PushSecret) => s.ciphertextKey !== '' && s.ciphertextValue !== ''
|
|
||||||
);
|
|
||||||
|
|
||||||
await push({
|
await push({
|
||||||
userId: req.user._id,
|
userId: req.user._id,
|
||||||
@@ -64,17 +62,16 @@ export const pushSecrets = async (req: Request, res: Response) => {
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
keys
|
keys
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: 'secrets pushed',
|
event: "secrets pushed",
|
||||||
distinctId: req.user.email,
|
distinctId: req.user.email,
|
||||||
properties: {
|
properties: {
|
||||||
numberOfSecrets: secrets.length,
|
numberOfSecrets: secrets.length,
|
||||||
environment,
|
environment,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
channel: channel ? channel : 'cli'
|
channel: channel ? channel : "cli"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -87,9 +84,9 @@ export const pushSecrets = async (req: Request, res: Response) => {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfully uploaded workspace secrets'
|
message: "Successfully uploaded workspace secrets"
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -100,57 +97,56 @@ export const pushSecrets = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const pullSecrets = async (req: Request, res: Response) => {
|
export const pullSecrets = async (req: Request, res: Response) => {
|
||||||
let secrets;
|
let secrets;
|
||||||
let key;
|
|
||||||
|
|
||||||
const postHogClient = await TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
const environment: string = req.query.environment as string;
|
const environment: string = req.query.environment as string;
|
||||||
const channel: string = req.query.channel as string;
|
const channel: string = req.query.channel as string;
|
||||||
const { workspaceId } = req.params;
|
const { workspaceId } = req.params;
|
||||||
|
|
||||||
// validate environment
|
// validate environment
|
||||||
const workspaceEnvs = req.membership.workspace.environments;
|
const workspaceEnvs = req.membership.workspace.environments;
|
||||||
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
|
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
|
||||||
throw new Error('Failed to validate environment');
|
throw new Error("Failed to validate environment");
|
||||||
}
|
}
|
||||||
|
|
||||||
secrets = await pull({
|
secrets = await pull({
|
||||||
userId: req.user._id.toString(),
|
userId: req.user._id.toString(),
|
||||||
workspaceId,
|
workspaceId,
|
||||||
environment,
|
environment,
|
||||||
channel: channel ? channel : 'cli',
|
channel: channel ? channel : "cli",
|
||||||
ipAddress: req.realIP
|
ipAddress: req.realIP
|
||||||
});
|
});
|
||||||
|
|
||||||
key = await Key.findOne({
|
const key = await Key.findOne({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
receiver: req.user._id
|
receiver: req.user._id
|
||||||
})
|
})
|
||||||
.sort({ createdAt: -1 })
|
.sort({ createdAt: -1 })
|
||||||
.populate('sender', '+publicKey');
|
.populate("sender", "+publicKey");
|
||||||
|
|
||||||
if (channel !== 'cli') {
|
|
||||||
secrets = reformatPullSecrets({ secrets });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (postHogClient) {
|
if (channel !== "cli") {
|
||||||
// capture secrets pushed event in production
|
secrets = reformatPullSecrets({ secrets });
|
||||||
postHogClient.capture({
|
}
|
||||||
distinctId: req.user.email,
|
|
||||||
event: 'secrets pulled',
|
|
||||||
properties: {
|
|
||||||
numberOfSecrets: secrets.length,
|
|
||||||
environment,
|
|
||||||
workspaceId,
|
|
||||||
channel: channel ? channel : 'cli'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.status(200).send({
|
if (postHogClient) {
|
||||||
secrets,
|
// capture secrets pushed event in production
|
||||||
key
|
postHogClient.capture({
|
||||||
});
|
distinctId: req.user.email,
|
||||||
|
event: "secrets pulled",
|
||||||
|
properties: {
|
||||||
|
numberOfSecrets: secrets.length,
|
||||||
|
environment,
|
||||||
|
workspaceId,
|
||||||
|
channel: channel ? channel : "cli"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
secrets,
|
||||||
|
key
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -162,54 +158,51 @@ export const pullSecrets = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const pullSecretsServiceToken = async (req: Request, res: Response) => {
|
export const pullSecretsServiceToken = async (req: Request, res: Response) => {
|
||||||
let secrets;
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
let key;
|
const environment: string = req.query.environment as string;
|
||||||
|
const channel: string = req.query.channel as string;
|
||||||
|
const { workspaceId } = req.params;
|
||||||
|
|
||||||
const postHogClient = await TelemetryService.getPostHogClient();
|
// validate environment
|
||||||
const environment: string = req.query.environment as string;
|
const workspaceEnvs = req.membership.workspace.environments;
|
||||||
const channel: string = req.query.channel as string;
|
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
|
||||||
const { workspaceId } = req.params;
|
throw new Error("Failed to validate environment");
|
||||||
|
}
|
||||||
|
|
||||||
// validate environment
|
const secrets = await pull({
|
||||||
const workspaceEnvs = req.membership.workspace.environments;
|
userId: req.serviceToken.user._id.toString(),
|
||||||
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
|
workspaceId,
|
||||||
throw new Error('Failed to validate environment');
|
environment,
|
||||||
}
|
channel: "cli",
|
||||||
|
ipAddress: req.realIP
|
||||||
|
});
|
||||||
|
|
||||||
secrets = await pull({
|
const key = {
|
||||||
userId: req.serviceToken.user._id.toString(),
|
encryptedKey: req.serviceToken.encryptedKey,
|
||||||
workspaceId,
|
nonce: req.serviceToken.nonce,
|
||||||
environment,
|
sender: {
|
||||||
channel: 'cli',
|
publicKey: req.serviceToken.publicKey
|
||||||
ipAddress: req.realIP
|
},
|
||||||
});
|
receiver: req.serviceToken.user,
|
||||||
|
workspace: req.serviceToken.workspace
|
||||||
|
};
|
||||||
|
|
||||||
key = {
|
if (postHogClient) {
|
||||||
encryptedKey: req.serviceToken.encryptedKey,
|
// capture secrets pulled event in production
|
||||||
nonce: req.serviceToken.nonce,
|
postHogClient.capture({
|
||||||
sender: {
|
distinctId: req.serviceToken.user.email,
|
||||||
publicKey: req.serviceToken.publicKey
|
event: "secrets pulled",
|
||||||
},
|
properties: {
|
||||||
receiver: req.serviceToken.user,
|
numberOfSecrets: secrets.length,
|
||||||
workspace: req.serviceToken.workspace
|
environment,
|
||||||
};
|
workspaceId,
|
||||||
|
channel: channel ? channel : "cli"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (postHogClient) {
|
return res.status(200).send({
|
||||||
// capture secrets pulled event in production
|
secrets: reformatPullSecrets({ secrets }),
|
||||||
postHogClient.capture({
|
key
|
||||||
distinctId: req.serviceToken.user.email,
|
});
|
||||||
event: 'secrets pulled',
|
|
||||||
properties: {
|
|
||||||
numberOfSecrets: secrets.length,
|
|
||||||
environment,
|
|
||||||
workspaceId,
|
|
||||||
channel: channel ? channel : 'cli'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.status(200).send({
|
|
||||||
secrets: reformatPullSecrets({ secrets }),
|
|
||||||
key
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ import { BadRequestError } from "../../utils/errors";
|
|||||||
import {
|
import {
|
||||||
appendFolder,
|
appendFolder,
|
||||||
deleteFolderById,
|
deleteFolderById,
|
||||||
getAllFolderIds,
|
|
||||||
searchByFolderIdWithDir,
|
|
||||||
searchByFolderId,
|
|
||||||
validateFolderName,
|
|
||||||
generateFolderId,
|
generateFolderId,
|
||||||
getParentFromFolderId,
|
getAllFolderIds,
|
||||||
getFolderByPath,
|
getFolderByPath,
|
||||||
|
getParentFromFolderId,
|
||||||
|
searchByFolderId,
|
||||||
|
searchByFolderIdWithDir,
|
||||||
|
validateFolderName,
|
||||||
} from "../../services/FolderService";
|
} from "../../services/FolderService";
|
||||||
import { ADMIN, MEMBER } from "../../variables";
|
import { ADMIN, MEMBER } from "../../variables";
|
||||||
import { validateMembership } from "../../helpers/membership";
|
import { validateMembership } from "../../helpers/membership";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import { ServiceToken } from '../../models';
|
import { ServiceToken } from "../../models";
|
||||||
import { createToken } from '../../helpers/auth';
|
import { createToken } from "../../helpers/auth";
|
||||||
import { getJwtServiceSecret } from '../../config';
|
import { getJwtServiceSecret } from "../../config";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return service token on request
|
* Return service token on request
|
||||||
@@ -11,7 +11,7 @@ import { getJwtServiceSecret } from '../../config';
|
|||||||
*/
|
*/
|
||||||
export const getServiceToken = async (req: Request, res: Response) => {
|
export const getServiceToken = async (req: Request, res: Response) => {
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
serviceToken: req.serviceToken
|
serviceToken: req.serviceToken,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -31,13 +31,13 @@ export const createServiceToken = async (req: Request, res: Response) => {
|
|||||||
expiresIn,
|
expiresIn,
|
||||||
publicKey,
|
publicKey,
|
||||||
encryptedKey,
|
encryptedKey,
|
||||||
nonce
|
nonce,
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
// validate environment
|
// validate environment
|
||||||
const workspaceEnvs = req.membership.workspace.environments;
|
const workspaceEnvs = req.membership.workspace.environments;
|
||||||
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
|
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
|
||||||
throw new Error('Failed to validate environment');
|
throw new Error("Failed to validate environment");
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute access token expiration date
|
// compute access token expiration date
|
||||||
@@ -52,24 +52,24 @@ export const createServiceToken = async (req: Request, res: Response) => {
|
|||||||
expiresAt,
|
expiresAt,
|
||||||
publicKey,
|
publicKey,
|
||||||
encryptedKey,
|
encryptedKey,
|
||||||
nonce
|
nonce,
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
token = createToken({
|
token = createToken({
|
||||||
payload: {
|
payload: {
|
||||||
serviceTokenId: serviceToken._id.toString(),
|
serviceTokenId: serviceToken._id.toString(),
|
||||||
workspaceId
|
workspaceId,
|
||||||
},
|
},
|
||||||
expiresIn: expiresIn,
|
expiresIn: expiresIn,
|
||||||
secret: await getJwtServiceSecret()
|
secret: await getJwtServiceSecret(),
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
message: 'Failed to create service token'
|
message: "Failed to create service token",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
token
|
token,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import { User } from '../../models';
|
import { User } from "../../models";
|
||||||
|
import { checkEmailVerification, sendEmailVerification } from "../../helpers/signup";
|
||||||
|
import { createToken } from "../../helpers/auth";
|
||||||
|
import { BadRequestError } from "../../utils/errors";
|
||||||
import {
|
import {
|
||||||
sendEmailVerification,
|
getInviteOnlySignup,
|
||||||
checkEmailVerification,
|
getJwtSignupLifetime,
|
||||||
} from '../../helpers/signup';
|
getJwtSignupSecret,
|
||||||
import { createToken } from '../../helpers/auth';
|
getSmtpConfigured
|
||||||
import { BadRequestError } from '../../utils/errors';
|
} from "../../config";
|
||||||
import { getInviteOnlySignup, getJwtSignupLifetime, getJwtSignupSecret, getSmtpConfigured } from '../../config';
|
import { validateUserEmail } from "../../validation";
|
||||||
import { validateUserEmail } from '../../validation';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signup step 1: Initialize account for user under email [email] and send a verification code
|
* Signup step 1: Initialize account for user under email [email] and send a verification code
|
||||||
@@ -17,27 +19,26 @@ import { validateUserEmail } from '../../validation';
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const beginEmailSignup = async (req: Request, res: Response) => {
|
export const beginEmailSignup = async (req: Request, res: Response) => {
|
||||||
let email: string;
|
const email: string = req.body.email;
|
||||||
email = req.body.email;
|
|
||||||
|
|
||||||
// validate that email is not disposable
|
// validate that email is not disposable
|
||||||
validateUserEmail(email);
|
validateUserEmail(email);
|
||||||
|
|
||||||
const user = await User.findOne({ email }).select('+publicKey');
|
const user = await User.findOne({ email }).select("+publicKey");
|
||||||
if (user && user?.publicKey) {
|
if (user && user?.publicKey) {
|
||||||
// case: user has already completed account
|
// case: user has already completed account
|
||||||
|
|
||||||
return res.status(403).send({
|
return res.status(403).send({
|
||||||
error: 'Failed to send email verification code for complete account'
|
error: "Failed to send email verification code for complete account"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// send send verification email
|
// send send verification email
|
||||||
await sendEmailVerification({ email });
|
await sendEmailVerification({ email });
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: `Sent an email verification code to ${email}`
|
message: `Sent an email verification code to ${email}`
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,23 +49,25 @@ export const beginEmailSignup = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const verifyEmailSignup = async (req: Request, res: Response) => {
|
export const verifyEmailSignup = async (req: Request, res: Response) => {
|
||||||
let user, token;
|
let user;
|
||||||
const { email, code } = req.body;
|
const { email, code } = req.body;
|
||||||
|
|
||||||
// initialize user account
|
// initialize user account
|
||||||
user = await User.findOne({ email }).select('+publicKey');
|
user = await User.findOne({ email }).select("+publicKey");
|
||||||
if (user && user?.publicKey) {
|
if (user && user?.publicKey) {
|
||||||
// case: user has already completed account
|
// case: user has already completed account
|
||||||
return res.status(403).send({
|
return res.status(403).send({
|
||||||
error: 'Failed email verification for complete user'
|
error: "Failed email verification for complete user"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await getInviteOnlySignup()) {
|
if (await getInviteOnlySignup()) {
|
||||||
// Only one user can create an account without being invited. The rest need to be invited in order to make an account
|
// Only one user can create an account without being invited. The rest need to be invited in order to make an account
|
||||||
const userCount = await User.countDocuments({})
|
const userCount = await User.countDocuments({});
|
||||||
if (userCount != 0) {
|
if (userCount != 0) {
|
||||||
throw BadRequestError({ message: "New user sign ups are not allowed at this time. You must be invited to sign up." })
|
throw BadRequestError({
|
||||||
|
message: "New user sign ups are not allowed at this time. You must be invited to sign up."
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +86,7 @@ export const verifyEmailSignup = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// generate temporary signup token
|
// generate temporary signup token
|
||||||
token = createToken({
|
const token = createToken({
|
||||||
payload: {
|
payload: {
|
||||||
userId: user._id.toString()
|
userId: user._id.toString()
|
||||||
},
|
},
|
||||||
@@ -91,9 +94,9 @@ export const verifyEmailSignup = async (req: Request, res: Response) => {
|
|||||||
secret: await getJwtSignupSecret()
|
secret: await getJwtSignupSecret()
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfuly verified email',
|
message: "Successfuly verified email",
|
||||||
user,
|
user,
|
||||||
token
|
token
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import Stripe from 'stripe';
|
import Stripe from "stripe";
|
||||||
import { getStripeSecretKey, getStripeWebhookSecret } from '../../config';
|
import { getStripeSecretKey, getStripeWebhookSecret } from "../../config";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle service provisioning/un-provisioning via Stripe
|
* Handle service provisioning/un-provisioning via Stripe
|
||||||
@@ -11,10 +11,10 @@ import { getStripeSecretKey, getStripeWebhookSecret } from '../../config';
|
|||||||
export const handleWebhook = async (req: Request, res: Response) => {
|
export const handleWebhook = async (req: Request, res: Response) => {
|
||||||
// check request for valid stripe signature
|
// check request for valid stripe signature
|
||||||
const stripe = new Stripe(await getStripeSecretKey(), {
|
const stripe = new Stripe(await getStripeSecretKey(), {
|
||||||
apiVersion: '2022-08-01'
|
apiVersion: "2022-08-01",
|
||||||
});
|
});
|
||||||
|
|
||||||
const sig = req.headers['stripe-signature'] as string;
|
const sig = req.headers["stripe-signature"] as string;
|
||||||
const event = stripe.webhooks.constructEvent(
|
const event = stripe.webhooks.constructEvent(
|
||||||
req.body,
|
req.body,
|
||||||
sig,
|
sig,
|
||||||
@@ -22,7 +22,7 @@ export const handleWebhook = async (req: Request, res: Response) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case '':
|
case "":
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import { UserAction } from '../../models';
|
import { UserAction } from "../../models";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add user action [action]
|
* Add user action [action]
|
||||||
@@ -15,18 +15,18 @@ export const addUserAction = async (req: Request, res: Response) => {
|
|||||||
const userAction = await UserAction.findOneAndUpdate(
|
const userAction = await UserAction.findOneAndUpdate(
|
||||||
{
|
{
|
||||||
user: req.user._id,
|
user: req.user._id,
|
||||||
action
|
action,
|
||||||
},
|
},
|
||||||
{ user: req.user._id, action },
|
{ user: req.user._id, action },
|
||||||
{
|
{
|
||||||
new: true,
|
new: true,
|
||||||
upsert: true
|
upsert: true,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfully recorded user action',
|
message: "Successfully recorded user action",
|
||||||
userAction
|
userAction,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -42,10 +42,10 @@ export const getUserAction = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
const userAction = await UserAction.findOne({
|
const userAction = await UserAction.findOne({
|
||||||
user: req.user._id,
|
user: req.user._id,
|
||||||
action
|
action,
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
userAction
|
userAction,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return user on request
|
* Return user on request
|
||||||
@@ -8,6 +8,6 @@ import { Request, Response } from 'express';
|
|||||||
*/
|
*/
|
||||||
export const getUser = async (req: Request, res: Response) => {
|
export const getUser = async (req: Request, res: Response) => {
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
user: req.user
|
user: req.user,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import {
|
import {
|
||||||
Workspace,
|
IUser,
|
||||||
Membership,
|
|
||||||
MembershipOrg,
|
|
||||||
Integration,
|
Integration,
|
||||||
IntegrationAuth,
|
IntegrationAuth,
|
||||||
IUser,
|
Membership,
|
||||||
|
MembershipOrg,
|
||||||
ServiceToken,
|
ServiceToken,
|
||||||
ServiceTokenData,
|
Workspace,
|
||||||
} from "../../models";
|
} from "../../models";
|
||||||
import {
|
import {
|
||||||
createWorkspace as create,
|
createWorkspace as create,
|
||||||
deleteWorkspace as deleteWork,
|
deleteWorkspace as deleteWork,
|
||||||
} from "../../helpers/workspace";
|
} from "../../helpers/workspace";
|
||||||
import { EELicenseService } from '../../ee/services';
|
import { EELicenseService } from "../../ee/services";
|
||||||
import { addMemberships } from "../../helpers/membership";
|
import { addMemberships } from "../../helpers/membership";
|
||||||
import { ADMIN } from "../../variables";
|
import { ADMIN } from "../../variables";
|
||||||
|
|
||||||
@@ -123,7 +122,7 @@ export const createWorkspace = async (req: Request, res: Response) => {
|
|||||||
if (plan.workspacesUsed >= plan.workspaceLimit) {
|
if (plan.workspacesUsed >= plan.workspaceLimit) {
|
||||||
// case: number of workspaces used exceeds the number of workspaces allowed
|
// case: number of workspaces used exceeds the number of workspaces allowed
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
message: 'Failed to create workspace due to plan limit reached. Upgrade plan to add more workspaces.'
|
message: "Failed to create workspace due to plan limit reached. Upgrade plan to add more workspaces.",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,74 +1,72 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import crypto from 'crypto';
|
import crypto from "crypto";
|
||||||
import bcrypt from 'bcrypt';
|
import bcrypt from "bcrypt";
|
||||||
import {
|
import { APIKeyData } from "../../models";
|
||||||
APIKeyData
|
import { getSaltRounds } from "../../config";
|
||||||
} from '../../models';
|
|
||||||
import { getSaltRounds } from '../../config';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return API key data for user with id [req.user_id]
|
* Return API key data for user with id [req.user_id]
|
||||||
* @param req
|
* @param req
|
||||||
* @param res
|
* @param res
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getAPIKeyData = async (req: Request, res: Response) => {
|
export const getAPIKeyData = async (req: Request, res: Response) => {
|
||||||
const apiKeyData = await APIKeyData.find({
|
const apiKeyData = await APIKeyData.find({
|
||||||
user: req.user._id
|
user: req.user._id,
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
apiKeyData
|
apiKeyData,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create new API key data for user with id [req.user._id]
|
* Create new API key data for user with id [req.user._id]
|
||||||
* @param req
|
* @param req
|
||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const createAPIKeyData = async (req: Request, res: Response) => {
|
export const createAPIKeyData = async (req: Request, res: Response) => {
|
||||||
const { name, expiresIn } = req.body;
|
const { name, expiresIn } = req.body;
|
||||||
|
|
||||||
const secret = crypto.randomBytes(16).toString('hex');
|
const secret = crypto.randomBytes(16).toString("hex");
|
||||||
const secretHash = await bcrypt.hash(secret, await getSaltRounds());
|
const secretHash = await bcrypt.hash(secret, await getSaltRounds());
|
||||||
|
|
||||||
const expiresAt = new Date();
|
const expiresAt = new Date();
|
||||||
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
|
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
|
||||||
|
|
||||||
let apiKeyData = await new APIKeyData({
|
let apiKeyData = await new APIKeyData({
|
||||||
name,
|
name,
|
||||||
lastUsed: new Date(),
|
lastUsed: new Date(),
|
||||||
expiresAt,
|
expiresAt,
|
||||||
user: req.user._id,
|
user: req.user._id,
|
||||||
secretHash
|
secretHash,
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
// return api key data without sensitive data
|
// return api key data without sensitive data
|
||||||
// FIX: fix this any
|
// FIX: fix this any
|
||||||
apiKeyData = await APIKeyData.findById(apiKeyData._id) as any
|
apiKeyData = (await APIKeyData.findById(apiKeyData._id)) as any;
|
||||||
|
|
||||||
if (!apiKeyData) throw new Error('Failed to find API key data');
|
if (!apiKeyData) throw new Error("Failed to find API key data");
|
||||||
|
|
||||||
const apiKey = `ak.${apiKeyData._id.toString()}.${secret}`;
|
const apiKey = `ak.${apiKeyData._id.toString()}.${secret}`;
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
apiKey,
|
apiKey,
|
||||||
apiKeyData
|
apiKeyData,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete API key data with id [apiKeyDataId].
|
* Delete API key data with id [apiKeyDataId].
|
||||||
* @param req
|
* @param req
|
||||||
* @param res
|
* @param res
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const deleteAPIKeyData = async (req: Request, res: Response) => {
|
export const deleteAPIKeyData = async (req: Request, res: Response) => {
|
||||||
const { apiKeyDataId } = req.params;
|
const { apiKeyDataId } = req.params;
|
||||||
const apiKeyData = await APIKeyData.findByIdAndDelete(apiKeyDataId);
|
const apiKeyData = await APIKeyData.findByIdAndDelete(apiKeyDataId);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
apiKeyData
|
apiKeyData,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from "jsonwebtoken";
|
||||||
import * as bigintConversion from 'bigint-conversion';
|
import * as bigintConversion from "bigint-conversion";
|
||||||
const jsrp = require('jsrp');
|
const jsrp = require("jsrp");
|
||||||
import { User, LoginSRPDetail } from '../../models';
|
import { LoginSRPDetail, User } from "../../models";
|
||||||
import { issueAuthTokens, createToken } from '../../helpers/auth';
|
import { createToken, issueAuthTokens } from "../../helpers/auth";
|
||||||
import { checkUserDevice } from '../../helpers/user';
|
import { checkUserDevice } from "../../helpers/user";
|
||||||
import { sendMail } from '../../helpers/nodemailer';
|
import { sendMail } from "../../helpers/nodemailer";
|
||||||
import { TokenService } from '../../services';
|
import { TokenService } from "../../services";
|
||||||
import { EELogService } from '../../ee/services';
|
import { EELogService } from "../../ee/services";
|
||||||
import { BadRequestError, InternalServerError } from '../../utils/errors';
|
import { BadRequestError, InternalServerError } from "../../utils/errors";
|
||||||
import {
|
import {
|
||||||
|
ACTION_LOGIN,
|
||||||
TOKEN_EMAIL_MFA,
|
TOKEN_EMAIL_MFA,
|
||||||
ACTION_LOGIN
|
} from "../../variables";
|
||||||
} from '../../variables';
|
import { getChannelFromUserAgent } from "../../utils/posthog"; // TODO: move this
|
||||||
import { getChannelFromUserAgent } from '../../utils/posthog'; // TODO: move this
|
|
||||||
import {
|
import {
|
||||||
|
getHttpsEnabled,
|
||||||
getJwtMfaLifetime,
|
getJwtMfaLifetime,
|
||||||
getJwtMfaSecret,
|
getJwtMfaSecret,
|
||||||
getHttpsEnabled
|
} from "../../config";
|
||||||
} from '../../config';
|
|
||||||
|
|
||||||
declare module 'jsonwebtoken' {
|
declare module "jsonwebtoken" {
|
||||||
export interface UserIDJwtPayload extends jwt.JwtPayload {
|
export interface UserIDJwtPayload extends jwt.JwtPayload {
|
||||||
userId: string;
|
userId: string;
|
||||||
}
|
}
|
||||||
@@ -36,20 +36,20 @@ declare module 'jsonwebtoken' {
|
|||||||
export const login1 = async (req: Request, res: Response) => {
|
export const login1 = async (req: Request, res: Response) => {
|
||||||
const {
|
const {
|
||||||
email,
|
email,
|
||||||
clientPublicKey
|
clientPublicKey,
|
||||||
}: { email: string; clientPublicKey: string } = req.body;
|
}: { email: string; clientPublicKey: string } = req.body;
|
||||||
|
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
email
|
email,
|
||||||
}).select('+salt +verifier');
|
}).select("+salt +verifier");
|
||||||
|
|
||||||
if (!user) throw new Error('Failed to find user');
|
if (!user) throw new Error("Failed to find user");
|
||||||
|
|
||||||
const server = new jsrp.server();
|
const server = new jsrp.server();
|
||||||
server.init(
|
server.init(
|
||||||
{
|
{
|
||||||
salt: user.salt,
|
salt: user.salt,
|
||||||
verifier: user.verifier
|
verifier: user.verifier,
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
// generate server-side public key
|
// generate server-side public key
|
||||||
@@ -63,7 +63,7 @@ export const login1 = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
serverPublicKey,
|
serverPublicKey,
|
||||||
salt: user.salt
|
salt: user.salt,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -78,14 +78,14 @@ export const login1 = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const login2 = async (req: Request, res: Response) => {
|
export const login2 = async (req: Request, res: Response) => {
|
||||||
if (!req.headers['user-agent']) throw InternalServerError({ message: 'User-Agent header is required' });
|
if (!req.headers["user-agent"]) throw InternalServerError({ message: "User-Agent header is required" });
|
||||||
|
|
||||||
const { email, clientProof } = req.body;
|
const { email, clientProof } = req.body;
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
email
|
email,
|
||||||
}).select('+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag +devices');
|
}).select("+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag +devices");
|
||||||
|
|
||||||
if (!user) throw new Error('Failed to find user');
|
if (!user) throw new Error("Failed to find user");
|
||||||
|
|
||||||
const loginSRPDetail = await LoginSRPDetail.findOneAndDelete({ email: email })
|
const loginSRPDetail = await LoginSRPDetail.findOneAndDelete({ email: email })
|
||||||
|
|
||||||
@@ -98,7 +98,7 @@ export const login2 = async (req: Request, res: Response) => {
|
|||||||
{
|
{
|
||||||
salt: user.salt,
|
salt: user.salt,
|
||||||
verifier: user.verifier,
|
verifier: user.verifier,
|
||||||
b: loginSRPDetail.serverBInt
|
b: loginSRPDetail.serverBInt,
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
server.setClientPublicKey(loginSRPDetail.clientPublicKey);
|
server.setClientPublicKey(loginSRPDetail.clientPublicKey);
|
||||||
@@ -111,52 +111,52 @@ export const login2 = async (req: Request, res: Response) => {
|
|||||||
// generate temporary MFA token
|
// generate temporary MFA token
|
||||||
const token = createToken({
|
const token = createToken({
|
||||||
payload: {
|
payload: {
|
||||||
userId: user._id.toString()
|
userId: user._id.toString(),
|
||||||
},
|
},
|
||||||
expiresIn: await getJwtMfaLifetime(),
|
expiresIn: await getJwtMfaLifetime(),
|
||||||
secret: await getJwtMfaSecret()
|
secret: await getJwtMfaSecret(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const code = await TokenService.createToken({
|
const code = await TokenService.createToken({
|
||||||
type: TOKEN_EMAIL_MFA,
|
type: TOKEN_EMAIL_MFA,
|
||||||
email
|
email,
|
||||||
});
|
});
|
||||||
|
|
||||||
// send MFA code [code] to [email]
|
// send MFA code [code] to [email]
|
||||||
await sendMail({
|
await sendMail({
|
||||||
template: 'emailMfa.handlebars',
|
template: "emailMfa.handlebars",
|
||||||
subjectLine: 'Infisical MFA code',
|
subjectLine: "Infisical MFA code",
|
||||||
recipients: [email],
|
recipients: [email],
|
||||||
substitutions: {
|
substitutions: {
|
||||||
code
|
code,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
mfaEnabled: true,
|
mfaEnabled: true,
|
||||||
token
|
token,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await checkUserDevice({
|
await checkUserDevice({
|
||||||
user,
|
user,
|
||||||
ip: req.realIP,
|
ip: req.realIP,
|
||||||
userAgent: req.headers['user-agent'] ?? ''
|
userAgent: req.headers["user-agent"] ?? "",
|
||||||
});
|
});
|
||||||
|
|
||||||
// issue tokens
|
// issue tokens
|
||||||
const tokens = await issueAuthTokens({
|
const tokens = await issueAuthTokens({
|
||||||
userId: user._id,
|
userId: user._id,
|
||||||
ip: req.realIP,
|
ip: req.realIP,
|
||||||
userAgent: req.headers['user-agent'] ?? ''
|
userAgent: req.headers["user-agent"] ?? "",
|
||||||
});
|
});
|
||||||
|
|
||||||
// store (refresh) token in httpOnly cookie
|
// store (refresh) token in httpOnly cookie
|
||||||
res.cookie('jid', tokens.refreshToken, {
|
res.cookie("jid", tokens.refreshToken, {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
path: '/',
|
path: "/",
|
||||||
sameSite: 'strict',
|
sameSite: "strict",
|
||||||
secure: await getHttpsEnabled()
|
secure: await getHttpsEnabled(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// case: user does not have MFA enabled
|
// case: user does not have MFA enabled
|
||||||
@@ -182,7 +182,7 @@ export const login2 = async (req: Request, res: Response) => {
|
|||||||
publicKey: user.publicKey,
|
publicKey: user.publicKey,
|
||||||
encryptedPrivateKey: user.encryptedPrivateKey,
|
encryptedPrivateKey: user.encryptedPrivateKey,
|
||||||
iv: user.iv,
|
iv: user.iv,
|
||||||
tag: user.tag
|
tag: user.tag,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -197,21 +197,21 @@ export const login2 = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
const loginAction = await EELogService.createAction({
|
const loginAction = await EELogService.createAction({
|
||||||
name: ACTION_LOGIN,
|
name: ACTION_LOGIN,
|
||||||
userId: user._id
|
userId: user._id,
|
||||||
});
|
});
|
||||||
|
|
||||||
loginAction && await EELogService.createLog({
|
loginAction && await EELogService.createLog({
|
||||||
userId: user._id,
|
userId: user._id,
|
||||||
actions: [loginAction],
|
actions: [loginAction],
|
||||||
channel: getChannelFromUserAgent(req.headers['user-agent']),
|
channel: getChannelFromUserAgent(req.headers["user-agent"]),
|
||||||
ipAddress: req.ip
|
ipAddress: req.ip,
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send(response);
|
return res.status(200).send(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
message: 'Failed to authenticate. Try again?'
|
message: "Failed to authenticate. Try again?",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -227,21 +227,21 @@ export const sendMfaToken = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
const code = await TokenService.createToken({
|
const code = await TokenService.createToken({
|
||||||
type: TOKEN_EMAIL_MFA,
|
type: TOKEN_EMAIL_MFA,
|
||||||
email
|
email,
|
||||||
});
|
});
|
||||||
|
|
||||||
// send MFA code [code] to [email]
|
// send MFA code [code] to [email]
|
||||||
await sendMail({
|
await sendMail({
|
||||||
template: 'emailMfa.handlebars',
|
template: "emailMfa.handlebars",
|
||||||
subjectLine: 'Infisical MFA code',
|
subjectLine: "Infisical MFA code",
|
||||||
recipients: [email],
|
recipients: [email],
|
||||||
substitutions: {
|
substitutions: {
|
||||||
code
|
code,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfully sent new MFA code'
|
message: "Successfully sent new MFA code",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,36 +257,36 @@ export const verifyMfaToken = async (req: Request, res: Response) => {
|
|||||||
await TokenService.validateToken({
|
await TokenService.validateToken({
|
||||||
type: TOKEN_EMAIL_MFA,
|
type: TOKEN_EMAIL_MFA,
|
||||||
email,
|
email,
|
||||||
token: mfaToken
|
token: mfaToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
email
|
email,
|
||||||
}).select('+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag +devices');
|
}).select("+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag +devices");
|
||||||
|
|
||||||
if (!user) throw new Error('Failed to find user');
|
if (!user) throw new Error("Failed to find user");
|
||||||
|
|
||||||
await LoginSRPDetail.deleteOne({ userId: user.id })
|
await LoginSRPDetail.deleteOne({ userId: user.id })
|
||||||
|
|
||||||
await checkUserDevice({
|
await checkUserDevice({
|
||||||
user,
|
user,
|
||||||
ip: req.realIP,
|
ip: req.realIP,
|
||||||
userAgent: req.headers['user-agent'] ?? ''
|
userAgent: req.headers["user-agent"] ?? "",
|
||||||
});
|
});
|
||||||
|
|
||||||
// issue tokens
|
// issue tokens
|
||||||
const tokens = await issueAuthTokens({
|
const tokens = await issueAuthTokens({
|
||||||
userId: user._id,
|
userId: user._id,
|
||||||
ip: req.realIP,
|
ip: req.realIP,
|
||||||
userAgent: req.headers['user-agent'] ?? ''
|
userAgent: req.headers["user-agent"] ?? "",
|
||||||
});
|
});
|
||||||
|
|
||||||
// store (refresh) token in httpOnly cookie
|
// store (refresh) token in httpOnly cookie
|
||||||
res.cookie('jid', tokens.refreshToken, {
|
res.cookie("jid", tokens.refreshToken, {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
path: '/',
|
path: "/",
|
||||||
sameSite: 'strict',
|
sameSite: "strict",
|
||||||
secure: await getHttpsEnabled()
|
secure: await getHttpsEnabled(),
|
||||||
});
|
});
|
||||||
|
|
||||||
interface VerifyMfaTokenRes {
|
interface VerifyMfaTokenRes {
|
||||||
@@ -319,7 +319,7 @@ export const verifyMfaToken = async (req: Request, res: Response) => {
|
|||||||
publicKey: user.publicKey as string,
|
publicKey: user.publicKey as string,
|
||||||
encryptedPrivateKey: user.encryptedPrivateKey as string,
|
encryptedPrivateKey: user.encryptedPrivateKey as string,
|
||||||
iv: user.iv as string,
|
iv: user.iv as string,
|
||||||
tag: user.tag as string
|
tag: user.tag as string,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user?.protectedKey && user?.protectedKeyIV && user?.protectedKeyTag) {
|
if (user?.protectedKey && user?.protectedKeyIV && user?.protectedKeyTag) {
|
||||||
@@ -330,14 +330,14 @@ export const verifyMfaToken = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
const loginAction = await EELogService.createAction({
|
const loginAction = await EELogService.createAction({
|
||||||
name: ACTION_LOGIN,
|
name: ACTION_LOGIN,
|
||||||
userId: user._id
|
userId: user._id,
|
||||||
});
|
});
|
||||||
|
|
||||||
loginAction && await EELogService.createLog({
|
loginAction && await EELogService.createLog({
|
||||||
userId: user._id,
|
userId: user._id,
|
||||||
actions: [loginAction],
|
actions: [loginAction],
|
||||||
channel: getChannelFromUserAgent(req.headers['user-agent']),
|
channel: getChannelFromUserAgent(req.headers["user-agent"]),
|
||||||
ipAddress: req.realIP
|
ipAddress: req.realIP,
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send(resObj);
|
return res.status(200).send(resObj);
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import {
|
import {
|
||||||
|
Integration,
|
||||||
|
Membership,
|
||||||
Secret,
|
Secret,
|
||||||
ServiceToken,
|
ServiceToken,
|
||||||
Workspace,
|
|
||||||
Integration,
|
|
||||||
ServiceTokenData,
|
ServiceTokenData,
|
||||||
Membership,
|
Workspace,
|
||||||
} from '../../models';
|
} from "../../models";
|
||||||
import { SecretVersion } from '../../ee/models';
|
import { SecretVersion } from "../../ee/models";
|
||||||
import { EELicenseService } from '../../ee/services';
|
import { EELicenseService } from "../../ee/services";
|
||||||
import { BadRequestError, WorkspaceNotFoundError } from '../../utils/errors';
|
import { BadRequestError, WorkspaceNotFoundError } from "../../utils/errors";
|
||||||
import _ from 'lodash';
|
import _ from "lodash";
|
||||||
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from '../../variables';
|
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../../variables";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create new workspace environment named [environmentName] under workspace with id
|
* Create new workspace environment named [environmentName] under workspace with id
|
||||||
@@ -38,7 +38,7 @@ export const createWorkspaceEnvironment = async (
|
|||||||
// case: number of environments used exceeds the number of environments allowed
|
// case: number of environments used exceeds the number of environments allowed
|
||||||
|
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
message: 'Failed to create environment due to environment limit reached. Upgrade plan to create more environments.'
|
message: "Failed to create environment due to environment limit reached. Upgrade plan to create more environments.",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,7 +49,7 @@ export const createWorkspaceEnvironment = async (
|
|||||||
({ name, slug }) => slug === environmentSlug || environmentName === name
|
({ name, slug }) => slug === environmentSlug || environmentName === name
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
throw new Error('Failed to create workspace environment');
|
throw new Error("Failed to create workspace environment");
|
||||||
}
|
}
|
||||||
|
|
||||||
workspace?.environments.push({
|
workspace?.environments.push({
|
||||||
@@ -61,7 +61,7 @@ export const createWorkspaceEnvironment = async (
|
|||||||
await EELicenseService.refreshPlan(workspace.organization.toString(), workspaceId);
|
await EELicenseService.refreshPlan(workspace.organization.toString(), workspaceId);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfully created new environment',
|
message: "Successfully created new environment",
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
environment: {
|
environment: {
|
||||||
name: environmentName,
|
name: environmentName,
|
||||||
@@ -85,13 +85,13 @@ export const renameWorkspaceEnvironment = async (
|
|||||||
const { environmentName, environmentSlug, oldEnvironmentSlug } = req.body;
|
const { environmentName, environmentSlug, oldEnvironmentSlug } = req.body;
|
||||||
// user should pass both new slug and env name
|
// user should pass both new slug and env name
|
||||||
if (!environmentSlug || !environmentName) {
|
if (!environmentSlug || !environmentName) {
|
||||||
throw new Error('Invalid environment given.');
|
throw new Error("Invalid environment given.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// atomic update the env to avoid conflict
|
// atomic update the env to avoid conflict
|
||||||
const workspace = await Workspace.findById(workspaceId).exec();
|
const workspace = await Workspace.findById(workspaceId).exec();
|
||||||
if (!workspace) {
|
if (!workspace) {
|
||||||
throw new Error('Failed to create workspace environment');
|
throw new Error("Failed to create workspace environment");
|
||||||
}
|
}
|
||||||
|
|
||||||
const isEnvExist = workspace.environments.some(
|
const isEnvExist = workspace.environments.some(
|
||||||
@@ -100,14 +100,14 @@ export const renameWorkspaceEnvironment = async (
|
|||||||
(name === environmentName || slug === environmentSlug)
|
(name === environmentName || slug === environmentSlug)
|
||||||
);
|
);
|
||||||
if (isEnvExist) {
|
if (isEnvExist) {
|
||||||
throw new Error('Invalid environment given');
|
throw new Error("Invalid environment given");
|
||||||
}
|
}
|
||||||
|
|
||||||
const envIndex = workspace?.environments.findIndex(
|
const envIndex = workspace?.environments.findIndex(
|
||||||
({ slug }) => slug === oldEnvironmentSlug
|
({ slug }) => slug === oldEnvironmentSlug
|
||||||
);
|
);
|
||||||
if (envIndex === -1) {
|
if (envIndex === -1) {
|
||||||
throw new Error('Invalid environment given');
|
throw new Error("Invalid environment given");
|
||||||
}
|
}
|
||||||
|
|
||||||
workspace.environments[envIndex].name = environmentName;
|
workspace.environments[envIndex].name = environmentName;
|
||||||
@@ -137,7 +137,7 @@ export const renameWorkspaceEnvironment = async (
|
|||||||
await Membership.updateMany(
|
await Membership.updateMany(
|
||||||
{
|
{
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
"deniedPermissions.environmentSlug": oldEnvironmentSlug
|
"deniedPermissions.environmentSlug": oldEnvironmentSlug,
|
||||||
},
|
},
|
||||||
{ $set: { "deniedPermissions.$[element].environmentSlug": environmentSlug } },
|
{ $set: { "deniedPermissions.$[element].environmentSlug": environmentSlug } },
|
||||||
{ arrayFilters: [{ "element.environmentSlug": oldEnvironmentSlug }] }
|
{ arrayFilters: [{ "element.environmentSlug": oldEnvironmentSlug }] }
|
||||||
@@ -145,7 +145,7 @@ export const renameWorkspaceEnvironment = async (
|
|||||||
|
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfully update environment',
|
message: "Successfully update environment",
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
environment: {
|
environment: {
|
||||||
name: environmentName,
|
name: environmentName,
|
||||||
@@ -169,14 +169,14 @@ export const deleteWorkspaceEnvironment = async (
|
|||||||
// atomic update the env to avoid conflict
|
// atomic update the env to avoid conflict
|
||||||
const workspace = await Workspace.findById(workspaceId).exec();
|
const workspace = await Workspace.findById(workspaceId).exec();
|
||||||
if (!workspace) {
|
if (!workspace) {
|
||||||
throw new Error('Failed to create workspace environment');
|
throw new Error("Failed to create workspace environment");
|
||||||
}
|
}
|
||||||
|
|
||||||
const envIndex = workspace?.environments.findIndex(
|
const envIndex = workspace?.environments.findIndex(
|
||||||
({ slug }) => slug === environmentSlug
|
({ slug }) => slug === environmentSlug
|
||||||
);
|
);
|
||||||
if (envIndex === -1) {
|
if (envIndex === -1) {
|
||||||
throw new Error('Invalid environment given');
|
throw new Error("Invalid environment given");
|
||||||
}
|
}
|
||||||
|
|
||||||
workspace.environments.splice(envIndex, 1);
|
workspace.environments.splice(envIndex, 1);
|
||||||
@@ -211,7 +211,7 @@ export const deleteWorkspaceEnvironment = async (
|
|||||||
await EELicenseService.refreshPlan(workspace.organization.toString(), workspaceId);
|
await EELicenseService.refreshPlan(workspace.organization.toString(), workspaceId);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfully deleted environment',
|
message: "Successfully deleted environment",
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
environment: environmentSlug,
|
environment: environmentSlug,
|
||||||
});
|
});
|
||||||
@@ -225,7 +225,7 @@ export const getAllAccessibleEnvironmentsOfWorkspace = async (
|
|||||||
const { workspaceId } = req.params;
|
const { workspaceId } = req.params;
|
||||||
const workspacesUserIsMemberOf = await Membership.findOne({
|
const workspacesUserIsMemberOf = await Membership.findOne({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
user: req.user
|
user: req.user,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!workspacesUserIsMemberOf) {
|
if (!workspacesUserIsMemberOf) {
|
||||||
@@ -249,7 +249,7 @@ export const getAllAccessibleEnvironmentsOfWorkspace = async (
|
|||||||
name: environment.name,
|
name: environment.name,
|
||||||
slug: environment.slug,
|
slug: environment.slug,
|
||||||
isWriteDenied: isWriteBlocked,
|
isWriteDenied: isWriteBlocked,
|
||||||
isReadDenied: isReadBlocked
|
isReadDenied: isReadBlocked,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import * as authController from './authController';
|
import * as authController from "./authController";
|
||||||
import * as signupController from './signupController';
|
import * as signupController from "./signupController";
|
||||||
import * as usersController from './usersController';
|
import * as usersController from "./usersController";
|
||||||
import * as organizationsController from './organizationsController';
|
import * as organizationsController from "./organizationsController";
|
||||||
import * as workspaceController from './workspaceController';
|
import * as workspaceController from "./workspaceController";
|
||||||
import * as serviceTokenDataController from './serviceTokenDataController';
|
import * as serviceTokenDataController from "./serviceTokenDataController";
|
||||||
import * as apiKeyDataController from './apiKeyDataController';
|
import * as apiKeyDataController from "./apiKeyDataController";
|
||||||
import * as secretController from './secretController';
|
import * as secretController from "./secretController";
|
||||||
import * as secretsController from './secretsController';
|
import * as secretsController from "./secretsController";
|
||||||
import * as serviceAccountsController from './serviceAccountsController';
|
import * as serviceAccountsController from "./serviceAccountsController";
|
||||||
import * as environmentController from './environmentController';
|
import * as environmentController from "./environmentController";
|
||||||
import * as tagController from './tagController';
|
import * as tagController from "./tagController";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
authController,
|
authController,
|
||||||
@@ -23,5 +23,5 @@ export {
|
|||||||
secretsController,
|
secretsController,
|
||||||
serviceAccountsController,
|
serviceAccountsController,
|
||||||
environmentController,
|
environmentController,
|
||||||
tagController
|
tagController,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import { Types } from 'mongoose';
|
import { Types } from "mongoose";
|
||||||
import {
|
import {
|
||||||
MembershipOrg,
|
|
||||||
Membership,
|
Membership,
|
||||||
|
MembershipOrg,
|
||||||
|
ServiceAccount,
|
||||||
Workspace,
|
Workspace,
|
||||||
ServiceAccount
|
} from "../../models";
|
||||||
} from '../../models';
|
import { deleteMembershipOrg } from "../../helpers/membershipOrg";
|
||||||
import { deleteMembershipOrg } from '../../helpers/membershipOrg';
|
import { updateSubscriptionOrgQuantity } from "../../helpers/organization";
|
||||||
import { updateSubscriptionOrgQuantity } from '../../helpers/organization';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return memberships for organization with id [organizationId]
|
* Return memberships for organization with id [organizationId]
|
||||||
@@ -51,11 +51,11 @@ export const getOrganizationMemberships = async (req: Request, res: Response) =>
|
|||||||
const { organizationId } = req.params;
|
const { organizationId } = req.params;
|
||||||
|
|
||||||
const memberships = await MembershipOrg.find({
|
const memberships = await MembershipOrg.find({
|
||||||
organization: organizationId
|
organization: organizationId,
|
||||||
}).populate('user', '+publicKey');
|
}).populate("user", "+publicKey");
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
memberships
|
memberships,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,14 +124,14 @@ export const updateOrganizationMembership = async (req: Request, res: Response)
|
|||||||
const membership = await MembershipOrg.findByIdAndUpdate(
|
const membership = await MembershipOrg.findByIdAndUpdate(
|
||||||
membershipId,
|
membershipId,
|
||||||
{
|
{
|
||||||
role
|
role,
|
||||||
}, {
|
}, {
|
||||||
new: true
|
new: true,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
membership
|
membership,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,15 +182,15 @@ export const deleteOrganizationMembership = async (req: Request, res: Response)
|
|||||||
|
|
||||||
// delete organization membership
|
// delete organization membership
|
||||||
const membership = await deleteMembershipOrg({
|
const membership = await deleteMembershipOrg({
|
||||||
membershipOrgId: membershipId
|
membershipOrgId: membershipId,
|
||||||
});
|
});
|
||||||
|
|
||||||
await updateSubscriptionOrgQuantity({
|
await updateSubscriptionOrgQuantity({
|
||||||
organizationId: membership.organization.toString()
|
organizationId: membership.organization.toString(),
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
membership
|
membership,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,23 +240,23 @@ export const getOrganizationWorkspaces = async (req: Request, res: Response) =>
|
|||||||
(
|
(
|
||||||
await Workspace.find(
|
await Workspace.find(
|
||||||
{
|
{
|
||||||
organization: organizationId
|
organization: organizationId,
|
||||||
},
|
},
|
||||||
'_id'
|
"_id"
|
||||||
)
|
)
|
||||||
).map((w) => w._id.toString())
|
).map((w) => w._id.toString())
|
||||||
);
|
);
|
||||||
|
|
||||||
const workspaces = (
|
const workspaces = (
|
||||||
await Membership.find({
|
await Membership.find({
|
||||||
user: req.user._id
|
user: req.user._id,
|
||||||
}).populate('workspace')
|
}).populate("workspace")
|
||||||
)
|
)
|
||||||
.filter((m) => workspacesSet.has(m.workspace._id.toString()))
|
.filter((m) => workspacesSet.has(m.workspace._id.toString()))
|
||||||
.map((m) => m.workspace);
|
.map((m) => m.workspace);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
workspaces
|
workspaces,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,10 +269,10 @@ export const getOrganizationServiceAccounts = async (req: Request, res: Response
|
|||||||
const { organizationId } = req.params;
|
const { organizationId } = req.params;
|
||||||
|
|
||||||
const serviceAccounts = await ServiceAccount.find({
|
const serviceAccounts = await ServiceAccount.find({
|
||||||
organization: new Types.ObjectId(organizationId)
|
organization: new Types.ObjectId(organizationId),
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
serviceAccounts
|
serviceAccounts,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,24 +2,39 @@ import to from "await-to-js";
|
|||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import mongoose, { Types } from "mongoose";
|
import mongoose, { Types } from "mongoose";
|
||||||
import Secret, { ISecret } from "../../models/secret";
|
import Secret, { ISecret } from "../../models/secret";
|
||||||
import { CreateSecretRequestBody, ModifySecretRequestBody, SanitizedSecretForCreate, SanitizedSecretModify } from "../../types/secret";
|
import {
|
||||||
|
CreateSecretRequestBody,
|
||||||
|
ModifySecretRequestBody,
|
||||||
|
SanitizedSecretForCreate,
|
||||||
|
SanitizedSecretModify
|
||||||
|
} from "../../types/secret";
|
||||||
const { ValidationError } = mongoose.Error;
|
const { ValidationError } = mongoose.Error;
|
||||||
import { BadRequestError, InternalServerError, UnauthorizedRequestError, ValidationError as RouteValidationError } from '../../utils/errors';
|
import {
|
||||||
import { AnyBulkWriteOperation } from 'mongodb';
|
BadRequestError,
|
||||||
import { ALGORITHM_AES_256_GCM, ENCODING_SCHEME_UTF8, SECRET_PERSONAL, SECRET_SHARED } from "../../variables";
|
InternalServerError,
|
||||||
import { TelemetryService } from '../../services';
|
ValidationError as RouteValidationError,
|
||||||
|
UnauthorizedRequestError
|
||||||
|
} from "../../utils/errors";
|
||||||
|
import { AnyBulkWriteOperation } from "mongodb";
|
||||||
|
import {
|
||||||
|
ALGORITHM_AES_256_GCM,
|
||||||
|
ENCODING_SCHEME_UTF8,
|
||||||
|
SECRET_PERSONAL,
|
||||||
|
SECRET_SHARED
|
||||||
|
} from "../../variables";
|
||||||
|
import { TelemetryService } from "../../services";
|
||||||
import { User } from "../../models";
|
import { User } from "../../models";
|
||||||
import { AccountNotFoundError } from '../../utils/errors';
|
import { AccountNotFoundError } from "../../utils/errors";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create secret for workspace with id [workspaceId] and environment [environment]
|
* Create secret for workspace with id [workspaceId] and environment [environment]
|
||||||
* @param req
|
* @param req
|
||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const createSecret = async (req: Request, res: Response) => {
|
export const createSecret = async (req: Request, res: Response) => {
|
||||||
const postHogClient = await TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
const secretToCreate: CreateSecretRequestBody = req.body.secret;
|
const secretToCreate: CreateSecretRequestBody = req.body.secret;
|
||||||
const { workspaceId, environment } = req.params
|
const { workspaceId, environment } = req.params;
|
||||||
const sanitizedSecret: SanitizedSecretForCreate = {
|
const sanitizedSecret: SanitizedSecretForCreate = {
|
||||||
secretKeyCiphertext: secretToCreate.secretKeyCiphertext,
|
secretKeyCiphertext: secretToCreate.secretKeyCiphertext,
|
||||||
secretKeyIV: secretToCreate.secretKeyIV,
|
secretKeyIV: secretToCreate.secretKeyIV,
|
||||||
@@ -39,45 +54,44 @@ export const createSecret = async (req: Request, res: Response) => {
|
|||||||
user: new Types.ObjectId(req.user._id),
|
user: new Types.ObjectId(req.user._id),
|
||||||
algorithm: ALGORITHM_AES_256_GCM,
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
keyEncoding: ENCODING_SCHEME_UTF8
|
keyEncoding: ENCODING_SCHEME_UTF8
|
||||||
}
|
};
|
||||||
|
|
||||||
|
const [error, secret] = await to(Secret.create(sanitizedSecret).then());
|
||||||
const [error, secret] = await to(Secret.create(sanitizedSecret).then())
|
|
||||||
if (error instanceof ValidationError) {
|
if (error instanceof ValidationError) {
|
||||||
throw RouteValidationError({ message: error.message, stack: error.stack })
|
throw RouteValidationError({ message: error.message, stack: error.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: 'secrets added',
|
event: "secrets added",
|
||||||
distinctId: req.user.email,
|
distinctId: req.user.email,
|
||||||
properties: {
|
properties: {
|
||||||
numberOfSecrets: 1,
|
numberOfSecrets: 1,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
environment,
|
environment,
|
||||||
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
|
channel: req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli",
|
||||||
userAgent: req.headers?.['user-agent']
|
userAgent: req.headers?.["user-agent"]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(200).send({
|
res.status(200).send({
|
||||||
secret
|
secret
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create many secrets for workspace wiht id [workspaceId] and environment [environment]
|
* Create many secrets for workspace wiht id [workspaceId] and environment [environment]
|
||||||
* @param req
|
* @param req
|
||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const createSecrets = async (req: Request, res: Response) => {
|
export const createSecrets = async (req: Request, res: Response) => {
|
||||||
const postHogClient = await TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
const secretsToCreate: CreateSecretRequestBody[] = req.body.secrets;
|
const secretsToCreate: CreateSecretRequestBody[] = req.body.secrets;
|
||||||
const { workspaceId, environment } = req.params
|
const { workspaceId, environment } = req.params;
|
||||||
const sanitizedSecretesToCreate: SanitizedSecretForCreate[] = []
|
const sanitizedSecretesToCreate: SanitizedSecretForCreate[] = [];
|
||||||
|
|
||||||
secretsToCreate.forEach(rawSecret => {
|
secretsToCreate.forEach((rawSecret) => {
|
||||||
const safeUpdateFields: SanitizedSecretForCreate = {
|
const safeUpdateFields: SanitizedSecretForCreate = {
|
||||||
secretKeyCiphertext: rawSecret.secretKeyCiphertext,
|
secretKeyCiphertext: rawSecret.secretKeyCiphertext,
|
||||||
secretKeyIV: rawSecret.secretKeyIV,
|
secretKeyIV: rawSecret.secretKeyIV,
|
||||||
@@ -97,140 +111,163 @@ export const createSecrets = async (req: Request, res: Response) => {
|
|||||||
user: new Types.ObjectId(req.user._id),
|
user: new Types.ObjectId(req.user._id),
|
||||||
algorithm: ALGORITHM_AES_256_GCM,
|
algorithm: ALGORITHM_AES_256_GCM,
|
||||||
keyEncoding: ENCODING_SCHEME_UTF8
|
keyEncoding: ENCODING_SCHEME_UTF8
|
||||||
}
|
};
|
||||||
|
|
||||||
sanitizedSecretesToCreate.push(safeUpdateFields)
|
sanitizedSecretesToCreate.push(safeUpdateFields);
|
||||||
})
|
});
|
||||||
|
|
||||||
const [bulkCreateError, secrets] = await to(Secret.insertMany(sanitizedSecretesToCreate).then())
|
const [bulkCreateError, secrets] = await to(Secret.insertMany(sanitizedSecretesToCreate).then());
|
||||||
if (bulkCreateError) {
|
if (bulkCreateError) {
|
||||||
if (bulkCreateError instanceof ValidationError) {
|
if (bulkCreateError instanceof ValidationError) {
|
||||||
throw RouteValidationError({ message: bulkCreateError.message, stack: bulkCreateError.stack })
|
throw RouteValidationError({
|
||||||
|
message: bulkCreateError.message,
|
||||||
|
stack: bulkCreateError.stack
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
throw InternalServerError({ message: "Unable to process your batch create request. Please try again", stack: bulkCreateError.stack })
|
throw InternalServerError({
|
||||||
|
message: "Unable to process your batch create request. Please try again",
|
||||||
|
stack: bulkCreateError.stack
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: 'secrets added',
|
event: "secrets added",
|
||||||
distinctId: req.user.email,
|
distinctId: req.user.email,
|
||||||
properties: {
|
properties: {
|
||||||
numberOfSecrets: (secretsToCreate ?? []).length,
|
numberOfSecrets: (secretsToCreate ?? []).length,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
environment,
|
environment,
|
||||||
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
|
channel: req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli",
|
||||||
userAgent: req.headers?.['user-agent']
|
userAgent: req.headers?.["user-agent"]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(200).send({
|
res.status(200).send({
|
||||||
secrets
|
secrets
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete secrets in workspace with id [workspaceId] and environment [environment]
|
* Delete secrets in workspace with id [workspaceId] and environment [environment]
|
||||||
* @param req
|
* @param req
|
||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const deleteSecrets = async (req: Request, res: Response) => {
|
export const deleteSecrets = async (req: Request, res: Response) => {
|
||||||
const postHogClient = await TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
const { workspaceId, environmentName } = req.params
|
const { workspaceId, environmentName } = req.params;
|
||||||
const secretIdsToDelete: string[] = req.body.secretIds
|
const secretIdsToDelete: string[] = req.body.secretIds;
|
||||||
|
|
||||||
const [secretIdsUserCanDeleteError, secretIdsUserCanDelete] = await to(Secret.find({ workspace: workspaceId, environment: environmentName }, { _id: 1 }).then())
|
const [secretIdsUserCanDeleteError, secretIdsUserCanDelete] = await to(
|
||||||
|
Secret.find({ workspace: workspaceId, environment: environmentName }, { _id: 1 }).then()
|
||||||
|
);
|
||||||
if (secretIdsUserCanDeleteError) {
|
if (secretIdsUserCanDeleteError) {
|
||||||
throw InternalServerError({ message: `Unable to fetch secrets you own: [error=${secretIdsUserCanDeleteError.message}]` })
|
throw InternalServerError({
|
||||||
|
message: `Unable to fetch secrets you own: [error=${secretIdsUserCanDeleteError.message}]`
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const secretsUserCanDeleteSet: Set<string> = new Set(secretIdsUserCanDelete.map(objectId => objectId._id.toString()));
|
const secretsUserCanDeleteSet: Set<string> = new Set(
|
||||||
const deleteOperationsToPerform: AnyBulkWriteOperation<ISecret>[] = []
|
secretIdsUserCanDelete.map((objectId) => objectId._id.toString())
|
||||||
|
);
|
||||||
|
const deleteOperationsToPerform: AnyBulkWriteOperation<ISecret>[] = [];
|
||||||
|
|
||||||
let numSecretsDeleted = 0;
|
let numSecretsDeleted = 0;
|
||||||
secretIdsToDelete.forEach(secretIdToDelete => {
|
secretIdsToDelete.forEach((secretIdToDelete) => {
|
||||||
if (secretsUserCanDeleteSet.has(secretIdToDelete)) {
|
if (secretsUserCanDeleteSet.has(secretIdToDelete)) {
|
||||||
const deleteOperation = { deleteOne: { filter: { _id: new Types.ObjectId(secretIdToDelete) } } }
|
const deleteOperation = {
|
||||||
deleteOperationsToPerform.push(deleteOperation)
|
deleteOne: { filter: { _id: new Types.ObjectId(secretIdToDelete) } }
|
||||||
|
};
|
||||||
|
deleteOperationsToPerform.push(deleteOperation);
|
||||||
numSecretsDeleted++;
|
numSecretsDeleted++;
|
||||||
} else {
|
} else {
|
||||||
throw RouteValidationError({ message: "You cannot delete secrets that you do not have access to" })
|
throw RouteValidationError({
|
||||||
|
message: "You cannot delete secrets that you do not have access to"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
const [bulkDeleteError, bulkDelete] = await to(Secret.bulkWrite(deleteOperationsToPerform).then())
|
const [bulkDeleteError] = await to(Secret.bulkWrite(deleteOperationsToPerform).then());
|
||||||
if (bulkDeleteError) {
|
if (bulkDeleteError) {
|
||||||
if (bulkDeleteError instanceof ValidationError) {
|
if (bulkDeleteError instanceof ValidationError) {
|
||||||
throw RouteValidationError({ message: "Unable to apply modifications, please try again", stack: bulkDeleteError.stack })
|
throw RouteValidationError({
|
||||||
|
message: "Unable to apply modifications, please try again",
|
||||||
|
stack: bulkDeleteError.stack
|
||||||
|
});
|
||||||
}
|
}
|
||||||
throw InternalServerError()
|
throw InternalServerError();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: 'secrets deleted',
|
event: "secrets deleted",
|
||||||
distinctId: req.user.email,
|
distinctId: req.user.email,
|
||||||
properties: {
|
properties: {
|
||||||
numberOfSecrets: numSecretsDeleted,
|
numberOfSecrets: numSecretsDeleted,
|
||||||
environment: environmentName,
|
environment: environmentName,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
|
channel: req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli",
|
||||||
userAgent: req.headers?.['user-agent']
|
userAgent: req.headers?.["user-agent"]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(200).send()
|
res.status(200).send();
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete secret with id [secretId]
|
* Delete secret with id [secretId]
|
||||||
* @param req
|
* @param req
|
||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const deleteSecret = async (req: Request, res: Response) => {
|
export const deleteSecret = async (req: Request, res: Response) => {
|
||||||
const postHogClient = await TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
await Secret.findByIdAndDelete(req._secret._id)
|
await Secret.findByIdAndDelete(req._secret._id);
|
||||||
|
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: 'secrets deleted',
|
event: "secrets deleted",
|
||||||
distinctId: req.user.email,
|
distinctId: req.user.email,
|
||||||
properties: {
|
properties: {
|
||||||
numberOfSecrets: 1,
|
numberOfSecrets: 1,
|
||||||
workspaceId: req._secret.workspace.toString(),
|
workspaceId: req._secret.workspace.toString(),
|
||||||
environment: req._secret.environment,
|
environment: req._secret.environment,
|
||||||
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
|
channel: req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli",
|
||||||
userAgent: req.headers?.['user-agent']
|
userAgent: req.headers?.["user-agent"]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(200).send({
|
res.status(200).send({
|
||||||
secret: req._secret
|
secret: req._secret
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update secrets for workspace with id [workspaceId] and environment [environment]
|
* Update secrets for workspace with id [workspaceId] and environment [environment]
|
||||||
* @param req
|
* @param req
|
||||||
* @param res
|
* @param res
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const updateSecrets = async (req: Request, res: Response) => {
|
export const updateSecrets = async (req: Request, res: Response) => {
|
||||||
const postHogClient = await TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
const { workspaceId, environmentName } = req.params
|
const { workspaceId, environmentName } = req.params;
|
||||||
const secretsModificationsRequested: ModifySecretRequestBody[] = req.body.secrets;
|
const secretsModificationsRequested: ModifySecretRequestBody[] = req.body.secrets;
|
||||||
const [secretIdsUserCanModifyError, secretIdsUserCanModify] = await to(Secret.find({ workspace: workspaceId, environment: environmentName }, { _id: 1 }).then())
|
const [secretIdsUserCanModifyError, secretIdsUserCanModify] = await to(
|
||||||
|
Secret.find({ workspace: workspaceId, environment: environmentName }, { _id: 1 }).then()
|
||||||
|
);
|
||||||
if (secretIdsUserCanModifyError) {
|
if (secretIdsUserCanModifyError) {
|
||||||
throw InternalServerError({ message: "Unable to fetch secrets you own" })
|
throw InternalServerError({ message: "Unable to fetch secrets you own" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const secretsUserCanModifySet: Set<string> = new Set(secretIdsUserCanModify.map(objectId => objectId._id.toString()));
|
const secretsUserCanModifySet: Set<string> = new Set(
|
||||||
const updateOperationsToPerform: any = []
|
secretIdsUserCanModify.map((objectId) => objectId._id.toString())
|
||||||
|
);
|
||||||
|
const updateOperationsToPerform: any = [];
|
||||||
|
|
||||||
secretsModificationsRequested.forEach(userModifiedSecret => {
|
secretsModificationsRequested.forEach((userModifiedSecret) => {
|
||||||
if (secretsUserCanModifySet.has(userModifiedSecret._id.toString())) {
|
if (secretsUserCanModifySet.has(userModifiedSecret._id.toString())) {
|
||||||
const sanitizedSecret: SanitizedSecretModify = {
|
const sanitizedSecret: SanitizedSecretModify = {
|
||||||
secretKeyCiphertext: userModifiedSecret.secretKeyCiphertext,
|
secretKeyCiphertext: userModifiedSecret.secretKeyCiphertext,
|
||||||
@@ -244,56 +281,70 @@ export const updateSecrets = async (req: Request, res: Response) => {
|
|||||||
secretCommentCiphertext: userModifiedSecret.secretCommentCiphertext,
|
secretCommentCiphertext: userModifiedSecret.secretCommentCiphertext,
|
||||||
secretCommentIV: userModifiedSecret.secretCommentIV,
|
secretCommentIV: userModifiedSecret.secretCommentIV,
|
||||||
secretCommentTag: userModifiedSecret.secretCommentTag,
|
secretCommentTag: userModifiedSecret.secretCommentTag,
|
||||||
secretCommentHash: userModifiedSecret.secretCommentHash,
|
secretCommentHash: userModifiedSecret.secretCommentHash
|
||||||
}
|
};
|
||||||
|
|
||||||
const updateOperation = { updateOne: { filter: { _id: userModifiedSecret._id, workspace: workspaceId }, update: { $inc: { version: 1 }, $set: sanitizedSecret } } }
|
const updateOperation = {
|
||||||
updateOperationsToPerform.push(updateOperation)
|
updateOne: {
|
||||||
|
filter: { _id: userModifiedSecret._id, workspace: workspaceId },
|
||||||
|
update: { $inc: { version: 1 }, $set: sanitizedSecret }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
updateOperationsToPerform.push(updateOperation);
|
||||||
} else {
|
} else {
|
||||||
throw UnauthorizedRequestError({ message: "You do not have permission to modify one or more of the requested secrets" })
|
throw UnauthorizedRequestError({
|
||||||
|
message: "You do not have permission to modify one or more of the requested secrets"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
const [bulkModificationInfoError, bulkModificationInfo] = await to(Secret.bulkWrite(updateOperationsToPerform).then())
|
const [bulkModificationInfoError, bulkModificationInfo] = await to(
|
||||||
|
Secret.bulkWrite(updateOperationsToPerform).then()
|
||||||
|
);
|
||||||
if (bulkModificationInfoError) {
|
if (bulkModificationInfoError) {
|
||||||
if (bulkModificationInfoError instanceof ValidationError) {
|
if (bulkModificationInfoError instanceof ValidationError) {
|
||||||
throw RouteValidationError({ message: "Unable to apply modifications, please try again", stack: bulkModificationInfoError.stack })
|
throw RouteValidationError({
|
||||||
|
message: "Unable to apply modifications, please try again",
|
||||||
|
stack: bulkModificationInfoError.stack
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
throw InternalServerError()
|
throw InternalServerError();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: 'secrets modified',
|
event: "secrets modified",
|
||||||
distinctId: req.user.email,
|
distinctId: req.user.email,
|
||||||
properties: {
|
properties: {
|
||||||
numberOfSecrets: (secretsModificationsRequested ?? []).length,
|
numberOfSecrets: (secretsModificationsRequested ?? []).length,
|
||||||
environment: environmentName,
|
environment: environmentName,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
|
channel: req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli",
|
||||||
userAgent: req.headers?.['user-agent']
|
userAgent: req.headers?.["user-agent"]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send()
|
return res.status(200).send();
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a secret within workspace with id [workspaceId] and environment [environment]
|
* Update a secret within workspace with id [workspaceId] and environment [environment]
|
||||||
* @param req
|
* @param req
|
||||||
* @param res
|
* @param res
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const updateSecret = async (req: Request, res: Response) => {
|
export const updateSecret = async (req: Request, res: Response) => {
|
||||||
const postHogClient = await TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
const { workspaceId, environmentName } = req.params
|
const { workspaceId, environmentName } = req.params;
|
||||||
const secretModificationsRequested: ModifySecretRequestBody = req.body.secret;
|
const secretModificationsRequested: ModifySecretRequestBody = req.body.secret;
|
||||||
|
|
||||||
const [secretIdUserCanModifyError, secretIdUserCanModify] = await to(Secret.findOne({ workspace: workspaceId, environment: environmentName }, { _id: 1 }).then())
|
const [secretIdUserCanModifyError, secretIdUserCanModify] = await to(
|
||||||
|
Secret.findOne({ workspace: workspaceId, environment: environmentName }, { _id: 1 }).then()
|
||||||
|
);
|
||||||
if (secretIdUserCanModifyError && !secretIdUserCanModify) {
|
if (secretIdUserCanModifyError && !secretIdUserCanModify) {
|
||||||
throw BadRequestError()
|
throw BadRequestError();
|
||||||
}
|
}
|
||||||
|
|
||||||
const sanitizedSecret: SanitizedSecretModify = {
|
const sanitizedSecret: SanitizedSecretModify = {
|
||||||
@@ -308,45 +359,53 @@ export const updateSecret = async (req: Request, res: Response) => {
|
|||||||
secretCommentCiphertext: secretModificationsRequested.secretCommentCiphertext,
|
secretCommentCiphertext: secretModificationsRequested.secretCommentCiphertext,
|
||||||
secretCommentIV: secretModificationsRequested.secretCommentIV,
|
secretCommentIV: secretModificationsRequested.secretCommentIV,
|
||||||
secretCommentTag: secretModificationsRequested.secretCommentTag,
|
secretCommentTag: secretModificationsRequested.secretCommentTag,
|
||||||
secretCommentHash: secretModificationsRequested.secretCommentHash,
|
secretCommentHash: secretModificationsRequested.secretCommentHash
|
||||||
}
|
};
|
||||||
|
|
||||||
const [error, singleModificationUpdate] = await to(Secret.updateOne({ _id: secretModificationsRequested._id, workspace: workspaceId }, { $inc: { version: 1 }, $set: sanitizedSecret }).then())
|
const [error, singleModificationUpdate] = await to(
|
||||||
|
Secret.updateOne(
|
||||||
|
{ _id: secretModificationsRequested._id, workspace: workspaceId },
|
||||||
|
{ $inc: { version: 1 }, $set: sanitizedSecret }
|
||||||
|
).then()
|
||||||
|
);
|
||||||
if (error instanceof ValidationError) {
|
if (error instanceof ValidationError) {
|
||||||
throw RouteValidationError({ message: "Unable to apply modifications, please try again", stack: error.stack })
|
throw RouteValidationError({
|
||||||
|
message: "Unable to apply modifications, please try again",
|
||||||
|
stack: error.stack
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: 'secrets modified',
|
event: "secrets modified",
|
||||||
distinctId: req.user.email,
|
distinctId: req.user.email,
|
||||||
properties: {
|
properties: {
|
||||||
numberOfSecrets: 1,
|
numberOfSecrets: 1,
|
||||||
environment: environmentName,
|
environment: environmentName,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
|
channel: req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli",
|
||||||
userAgent: req.headers?.['user-agent']
|
userAgent: req.headers?.["user-agent"]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send(singleModificationUpdate)
|
return res.status(200).send(singleModificationUpdate);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return secrets for workspace with id [workspaceId], environment [environment] and user
|
* Return secrets for workspace with id [workspaceId], environment [environment] and user
|
||||||
* with id [req.user._id]
|
* with id [req.user._id]
|
||||||
* @param req
|
* @param req
|
||||||
* @param res
|
* @param res
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getSecrets = async (req: Request, res: Response) => {
|
export const getSecrets = async (req: Request, res: Response) => {
|
||||||
const postHogClient = await TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
const { environment } = req.query;
|
const { environment } = req.query;
|
||||||
const { workspaceId } = req.params;
|
const { workspaceId } = req.params;
|
||||||
|
|
||||||
let userId: Types.ObjectId | undefined = undefined // used for getting personal secrets for user
|
let userId: Types.ObjectId | undefined = undefined; // used for getting personal secrets for user
|
||||||
let userEmail: string | undefined = undefined // used for posthog
|
let userEmail: string | undefined = undefined; // used for posthog
|
||||||
if (req.user) {
|
if (req.user) {
|
||||||
userId = req.user._id;
|
userId = req.user._id;
|
||||||
userEmail = req.user.email;
|
userEmail = req.user.email;
|
||||||
@@ -354,47 +413,50 @@ export const getSecrets = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
if (req.serviceTokenData) {
|
if (req.serviceTokenData) {
|
||||||
userId = req.serviceTokenData.user;
|
userId = req.serviceTokenData.user;
|
||||||
|
|
||||||
const user = await User.findById(req.serviceTokenData.user, 'email');
|
const user = await User.findById(req.serviceTokenData.user, "email");
|
||||||
if (!user) throw AccountNotFoundError();
|
if (!user) throw AccountNotFoundError();
|
||||||
userEmail = user.email;
|
userEmail = user.email;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [err, secrets] = await to(Secret.find(
|
const [err, secrets] = await to(
|
||||||
{
|
Secret.find({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
environment,
|
environment,
|
||||||
$or: [{ user: userId }, { user: { $exists: false } }],
|
$or: [{ user: userId }, { user: { $exists: false } }],
|
||||||
type: { $in: [SECRET_SHARED, SECRET_PERSONAL] }
|
type: { $in: [SECRET_SHARED, SECRET_PERSONAL] }
|
||||||
}
|
}).then()
|
||||||
).then())
|
);
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
throw RouteValidationError({ message: "Failed to get secrets, please try again", stack: err.stack })
|
throw RouteValidationError({
|
||||||
|
message: "Failed to get secrets, please try again",
|
||||||
|
stack: err.stack
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: 'secrets pulled',
|
event: "secrets pulled",
|
||||||
distinctId: userEmail,
|
distinctId: userEmail,
|
||||||
properties: {
|
properties: {
|
||||||
numberOfSecrets: (secrets ?? []).length,
|
numberOfSecrets: (secrets ?? []).length,
|
||||||
environment,
|
environment,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
|
channel: req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli",
|
||||||
userAgent: req.headers?.['user-agent']
|
userAgent: req.headers?.["user-agent"]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.json(secrets)
|
return res.json(secrets);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return secret with id [secretId]
|
* Return secret with id [secretId]
|
||||||
* @param req
|
* @param req
|
||||||
* @param res
|
* @param res
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getSecret = async (req: Request, res: Response) => {
|
export const getSecret = async (req: Request, res: Response) => {
|
||||||
// if (postHogClient) {
|
// if (postHogClient) {
|
||||||
@@ -414,4 +476,4 @@ export const getSecret = async (req: Request, res: Response) => {
|
|||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
secret: req._secret
|
secret: req._secret
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -3,19 +3,19 @@ import { Request, Response } from "express";
|
|||||||
import { ISecret, Secret, ServiceTokenData } from "../../models";
|
import { ISecret, Secret, ServiceTokenData } from "../../models";
|
||||||
import { IAction, SecretVersion } from "../../ee/models";
|
import { IAction, SecretVersion } from "../../ee/models";
|
||||||
import {
|
import {
|
||||||
SECRET_PERSONAL,
|
|
||||||
ACTION_ADD_SECRETS,
|
ACTION_ADD_SECRETS,
|
||||||
|
ACTION_DELETE_SECRETS,
|
||||||
ACTION_READ_SECRETS,
|
ACTION_READ_SECRETS,
|
||||||
ACTION_UPDATE_SECRETS,
|
ACTION_UPDATE_SECRETS,
|
||||||
ACTION_DELETE_SECRETS,
|
|
||||||
ALGORITHM_AES_256_GCM,
|
ALGORITHM_AES_256_GCM,
|
||||||
ENCODING_SCHEME_UTF8,
|
ENCODING_SCHEME_UTF8,
|
||||||
|
SECRET_PERSONAL,
|
||||||
} from "../../variables";
|
} from "../../variables";
|
||||||
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
|
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
|
||||||
import { EventService } from "../../services";
|
import { EventService } from "../../services";
|
||||||
import { eventPushSecrets } from "../../events";
|
import { eventPushSecrets } from "../../events";
|
||||||
import { EESecretService, EELogService } from "../../ee/services";
|
import { EELogService, EESecretService } from "../../ee/services";
|
||||||
import { TelemetryService, SecretService } from "../../services";
|
import { SecretService, TelemetryService } from "../../services";
|
||||||
import { getChannelFromUserAgent } from "../../utils/posthog";
|
import { getChannelFromUserAgent } from "../../utils/posthog";
|
||||||
import { PERMISSION_WRITE_SECRETS } from "../../variables";
|
import { PERMISSION_WRITE_SECRETS } from "../../variables";
|
||||||
import {
|
import {
|
||||||
@@ -25,7 +25,7 @@ import {
|
|||||||
} from "../../ee/helpers/checkMembershipPermissions";
|
} from "../../ee/helpers/checkMembershipPermissions";
|
||||||
import Tag from "../../models/tag";
|
import Tag from "../../models/tag";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { BatchSecretRequest, BatchSecret } from "../../types/secret";
|
import { BatchSecret, BatchSecretRequest } from "../../types/secret";
|
||||||
import Folder from "../../models/folder";
|
import Folder from "../../models/folder";
|
||||||
import {
|
import {
|
||||||
getFolderByPath,
|
getFolderByPath,
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import { Types } from 'mongoose';
|
import { Types } from "mongoose";
|
||||||
import crypto from 'crypto';
|
import crypto from "crypto";
|
||||||
import bcrypt from 'bcrypt';
|
import bcrypt from "bcrypt";
|
||||||
import {
|
import {
|
||||||
ServiceAccount,
|
ServiceAccount,
|
||||||
ServiceAccountKey,
|
ServiceAccountKey,
|
||||||
ServiceAccountOrganizationPermission,
|
ServiceAccountOrganizationPermission,
|
||||||
ServiceAccountWorkspacePermission
|
ServiceAccountWorkspacePermission,
|
||||||
} from '../../models';
|
} from "../../models";
|
||||||
import {
|
import {
|
||||||
CreateServiceAccountDto
|
CreateServiceAccountDto,
|
||||||
} from '../../interfaces/serviceAccounts/dto';
|
} from "../../interfaces/serviceAccounts/dto";
|
||||||
import { BadRequestError, ServiceAccountNotFoundError } from '../../utils/errors';
|
import { BadRequestError, ServiceAccountNotFoundError } from "../../utils/errors";
|
||||||
import { getSaltRounds } from '../../config';
|
import { getSaltRounds } from "../../config";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return service account tied to the request (service account) client
|
* Return service account tied to the request (service account) client
|
||||||
@@ -23,11 +23,11 @@ export const getCurrentServiceAccount = async (req: Request, res: Response) => {
|
|||||||
const serviceAccount = await ServiceAccount.findById(req.serviceAccount._id);
|
const serviceAccount = await ServiceAccount.findById(req.serviceAccount._id);
|
||||||
|
|
||||||
if (!serviceAccount) {
|
if (!serviceAccount) {
|
||||||
throw ServiceAccountNotFoundError({ message: 'Failed to find service account' });
|
throw ServiceAccountNotFoundError({ message: "Failed to find service account" });
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
serviceAccount
|
serviceAccount,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,11 +42,11 @@ export const getServiceAccountById = async (req: Request, res: Response) => {
|
|||||||
const serviceAccount = await ServiceAccount.findById(serviceAccountId);
|
const serviceAccount = await ServiceAccount.findById(serviceAccountId);
|
||||||
|
|
||||||
if (!serviceAccount) {
|
if (!serviceAccount) {
|
||||||
throw ServiceAccountNotFoundError({ message: 'Failed to find service account' });
|
throw ServiceAccountNotFoundError({ message: "Failed to find service account" });
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
serviceAccount
|
serviceAccount,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ export const createServiceAccount = async (req: Request, res: Response) => {
|
|||||||
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
|
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
|
||||||
}
|
}
|
||||||
|
|
||||||
const secret = crypto.randomBytes(16).toString('base64');
|
const secret = crypto.randomBytes(16).toString("base64");
|
||||||
const secretHash = await bcrypt.hash(secret, await getSaltRounds());
|
const secretHash = await bcrypt.hash(secret, await getSaltRounds());
|
||||||
|
|
||||||
// create service account
|
// create service account
|
||||||
@@ -82,7 +82,7 @@ export const createServiceAccount = async (req: Request, res: Response) => {
|
|||||||
publicKey,
|
publicKey,
|
||||||
lastUsed: new Date(),
|
lastUsed: new Date(),
|
||||||
expiresAt,
|
expiresAt,
|
||||||
secretHash
|
secretHash,
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
const serviceAccountObj = serviceAccount.toObject();
|
const serviceAccountObj = serviceAccount.toObject();
|
||||||
@@ -91,14 +91,14 @@ export const createServiceAccount = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
// provision default org-level permission for service account
|
// provision default org-level permission for service account
|
||||||
await new ServiceAccountOrganizationPermission({
|
await new ServiceAccountOrganizationPermission({
|
||||||
serviceAccount: serviceAccount._id
|
serviceAccount: serviceAccount._id,
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
const secretId = Buffer.from(serviceAccount._id.toString(), 'hex').toString('base64');
|
const secretId = Buffer.from(serviceAccount._id.toString(), "hex").toString("base64");
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
serviceAccountAccessKey: `sa.${secretId}.${secret}`,
|
serviceAccountAccessKey: `sa.${secretId}.${secret}`,
|
||||||
serviceAccount: serviceAccountObj
|
serviceAccount: serviceAccountObj,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,18 +114,18 @@ export const changeServiceAccountName = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
const serviceAccount = await ServiceAccount.findOneAndUpdate(
|
const serviceAccount = await ServiceAccount.findOneAndUpdate(
|
||||||
{
|
{
|
||||||
_id: new Types.ObjectId(serviceAccountId)
|
_id: new Types.ObjectId(serviceAccountId),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name
|
name,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
new: true
|
new: true,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
serviceAccount
|
serviceAccount,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@ export const addServiceAccountKey = async (req: Request, res: Response) => {
|
|||||||
const {
|
const {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
encryptedKey,
|
encryptedKey,
|
||||||
nonce
|
nonce,
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
const serviceAccountKey = await new ServiceAccountKey({
|
const serviceAccountKey = await new ServiceAccountKey({
|
||||||
@@ -148,7 +148,7 @@ export const addServiceAccountKey = async (req: Request, res: Response) => {
|
|||||||
nonce,
|
nonce,
|
||||||
sender: req.user._id,
|
sender: req.user._id,
|
||||||
serviceAccount: req.serviceAccount._d,
|
serviceAccount: req.serviceAccount._d,
|
||||||
workspace: new Types.ObjectId(workspaceId)
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
return serviceAccountKey;
|
return serviceAccountKey;
|
||||||
@@ -161,11 +161,11 @@ export const addServiceAccountKey = async (req: Request, res: Response) => {
|
|||||||
*/
|
*/
|
||||||
export const getServiceAccountWorkspacePermissions = async (req: Request, res: Response) => {
|
export const getServiceAccountWorkspacePermissions = async (req: Request, res: Response) => {
|
||||||
const serviceAccountWorkspacePermissions = await ServiceAccountWorkspacePermission.find({
|
const serviceAccountWorkspacePermissions = await ServiceAccountWorkspacePermission.find({
|
||||||
serviceAccount: req.serviceAccount._id
|
serviceAccount: req.serviceAccount._id,
|
||||||
}).populate('workspace');
|
}).populate("workspace");
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
serviceAccountWorkspacePermissions
|
serviceAccountWorkspacePermissions,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,34 +182,34 @@ export const addServiceAccountWorkspacePermission = async (req: Request, res: Re
|
|||||||
read = false,
|
read = false,
|
||||||
write = false,
|
write = false,
|
||||||
encryptedKey,
|
encryptedKey,
|
||||||
nonce
|
nonce,
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
if (!req.membership.workspace.environments.some((e: { name: string; slug: string }) => e.slug === environment)) {
|
if (!req.membership.workspace.environments.some((e: { name: string; slug: string }) => e.slug === environment)) {
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
message: 'Failed to validate workspace environment'
|
message: "Failed to validate workspace environment",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingPermission = await ServiceAccountWorkspacePermission.findOne({
|
const existingPermission = await ServiceAccountWorkspacePermission.findOne({
|
||||||
serviceAccount: new Types.ObjectId(serviceAccountId),
|
serviceAccount: new Types.ObjectId(serviceAccountId),
|
||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
environment
|
environment,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (existingPermission) throw BadRequestError({ message: 'Failed to add workspace permission to service account due to already-existing ' });
|
if (existingPermission) throw BadRequestError({ message: "Failed to add workspace permission to service account due to already-existing " });
|
||||||
|
|
||||||
const serviceAccountWorkspacePermission = await new ServiceAccountWorkspacePermission({
|
const serviceAccountWorkspacePermission = await new ServiceAccountWorkspacePermission({
|
||||||
serviceAccount: new Types.ObjectId(serviceAccountId),
|
serviceAccount: new Types.ObjectId(serviceAccountId),
|
||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
read,
|
read,
|
||||||
write
|
write,
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
const existingServiceAccountKey = await ServiceAccountKey.findOne({
|
const existingServiceAccountKey = await ServiceAccountKey.findOne({
|
||||||
serviceAccount: new Types.ObjectId(serviceAccountId),
|
serviceAccount: new Types.ObjectId(serviceAccountId),
|
||||||
workspace: new Types.ObjectId(workspaceId)
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!existingServiceAccountKey) {
|
if (!existingServiceAccountKey) {
|
||||||
@@ -218,12 +218,12 @@ export const addServiceAccountWorkspacePermission = async (req: Request, res: Re
|
|||||||
nonce,
|
nonce,
|
||||||
sender: req.user._id,
|
sender: req.user._id,
|
||||||
serviceAccount: new Types.ObjectId(serviceAccountId),
|
serviceAccount: new Types.ObjectId(serviceAccountId),
|
||||||
workspace: new Types.ObjectId(workspaceId)
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
}).save();
|
}).save();
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
serviceAccountWorkspacePermission
|
serviceAccountWorkspacePermission,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,19 +240,19 @@ export const deleteServiceAccountWorkspacePermission = async (req: Request, res:
|
|||||||
const { serviceAccount, workspace } = serviceAccountWorkspacePermission;
|
const { serviceAccount, workspace } = serviceAccountWorkspacePermission;
|
||||||
const count = await ServiceAccountWorkspacePermission.countDocuments({
|
const count = await ServiceAccountWorkspacePermission.countDocuments({
|
||||||
serviceAccount,
|
serviceAccount,
|
||||||
workspace
|
workspace,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (count === 0) {
|
if (count === 0) {
|
||||||
await ServiceAccountKey.findOneAndDelete({
|
await ServiceAccountKey.findOneAndDelete({
|
||||||
serviceAccount,
|
serviceAccount,
|
||||||
workspace
|
workspace,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
serviceAccountWorkspacePermission
|
serviceAccountWorkspacePermission,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,20 +269,20 @@ export const deleteServiceAccount = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
if (serviceAccount) {
|
if (serviceAccount) {
|
||||||
await ServiceAccountKey.deleteMany({
|
await ServiceAccountKey.deleteMany({
|
||||||
serviceAccount: serviceAccount._id
|
serviceAccount: serviceAccount._id,
|
||||||
});
|
});
|
||||||
|
|
||||||
await ServiceAccountOrganizationPermission.deleteMany({
|
await ServiceAccountOrganizationPermission.deleteMany({
|
||||||
serviceAccount: new Types.ObjectId(serviceAccountId)
|
serviceAccount: new Types.ObjectId(serviceAccountId),
|
||||||
});
|
});
|
||||||
|
|
||||||
await ServiceAccountWorkspacePermission.deleteMany({
|
await ServiceAccountWorkspacePermission.deleteMany({
|
||||||
serviceAccount: new Types.ObjectId(serviceAccountId)
|
serviceAccount: new Types.ObjectId(serviceAccountId),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
serviceAccount
|
serviceAccount,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,10 +297,10 @@ export const getServiceAccountKeys = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
const serviceAccountKeys = await ServiceAccountKey.find({
|
const serviceAccountKeys = await ServiceAccountKey.find({
|
||||||
serviceAccount: req.serviceAccount._id,
|
serviceAccount: req.serviceAccount._id,
|
||||||
...(workspaceId ? { workspace: new Types.ObjectId(workspaceId) } : {})
|
...(workspaceId ? { workspace: new Types.ObjectId(workspaceId) } : {}),
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
serviceAccountKeys
|
serviceAccountKeys,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,10 @@
|
|||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
import bcrypt from "bcrypt";
|
import bcrypt from "bcrypt";
|
||||||
import { User, ServiceAccount, ServiceTokenData } from "../../models";
|
import { ServiceAccount, ServiceTokenData, User } from "../../models";
|
||||||
import { userHasWorkspaceAccess } from "../../ee/helpers/checkMembershipPermissions";
|
|
||||||
import {
|
import {
|
||||||
PERMISSION_READ_SECRETS,
|
|
||||||
AUTH_MODE_JWT,
|
AUTH_MODE_JWT,
|
||||||
AUTH_MODE_SERVICE_ACCOUNT,
|
AUTH_MODE_SERVICE_ACCOUNT,
|
||||||
AUTH_MODE_SERVICE_TOKEN,
|
|
||||||
} from "../../variables";
|
} from "../../variables";
|
||||||
import { getSaltRounds } from "../../config";
|
import { getSaltRounds } from "../../config";
|
||||||
import { BadRequestError } from "../../utils/errors";
|
import { BadRequestError } from "../../utils/errors";
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import { User, MembershipOrg } from '../../models';
|
import { MembershipOrg, User } from "../../models";
|
||||||
import { completeAccount } from '../../helpers/user';
|
import { completeAccount } from "../../helpers/user";
|
||||||
import {
|
import {
|
||||||
initializeDefaultOrg
|
initializeDefaultOrg,
|
||||||
} from '../../helpers/signup';
|
} from "../../helpers/signup";
|
||||||
import { issueAuthTokens } from '../../helpers/auth';
|
import { issueAuthTokens } from "../../helpers/auth";
|
||||||
import { INVITED, ACCEPTED } from '../../variables';
|
import { ACCEPTED, INVITED } from "../../variables";
|
||||||
import { standardRequest } from '../../config/request';
|
import { standardRequest } from "../../config/request";
|
||||||
import { getLoopsApiKey, getHttpsEnabled } from '../../config';
|
import { getHttpsEnabled, getLoopsApiKey } from "../../config";
|
||||||
import { updateSubscriptionOrgQuantity } from '../../helpers/organization';
|
import { updateSubscriptionOrgQuantity } from "../../helpers/organization";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Complete setting up user by adding their personal and auth information as part of the
|
* Complete setting up user by adding their personal and auth information as part of the
|
||||||
@@ -32,7 +32,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
|||||||
encryptedPrivateKeyTag,
|
encryptedPrivateKeyTag,
|
||||||
salt,
|
salt,
|
||||||
verifier,
|
verifier,
|
||||||
organizationName
|
organizationName,
|
||||||
}: {
|
}: {
|
||||||
email: string;
|
email: string;
|
||||||
firstName: string;
|
firstName: string;
|
||||||
@@ -56,7 +56,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
|||||||
// case 1: user doesn't exist.
|
// case 1: user doesn't exist.
|
||||||
// case 2: user has already completed account
|
// case 2: user has already completed account
|
||||||
return res.status(403).send({
|
return res.status(403).send({
|
||||||
error: 'Failed to complete account for complete user'
|
error: "Failed to complete account for complete user",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,28 +74,28 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
|||||||
encryptedPrivateKeyIV,
|
encryptedPrivateKeyIV,
|
||||||
encryptedPrivateKeyTag,
|
encryptedPrivateKeyTag,
|
||||||
salt,
|
salt,
|
||||||
verifier
|
verifier,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user)
|
if (!user)
|
||||||
throw new Error('Failed to complete account for non-existent user'); // ensure user is non-null
|
throw new Error("Failed to complete account for non-existent user"); // ensure user is non-null
|
||||||
|
|
||||||
// initialize default organization and workspace
|
// initialize default organization and workspace
|
||||||
await initializeDefaultOrg({
|
await initializeDefaultOrg({
|
||||||
organizationName,
|
organizationName,
|
||||||
user
|
user,
|
||||||
});
|
});
|
||||||
|
|
||||||
// update organization membership statuses that are
|
// update organization membership statuses that are
|
||||||
// invited to completed with user attached
|
// invited to completed with user attached
|
||||||
const membershipsToUpdate = await MembershipOrg.find({
|
const membershipsToUpdate = await MembershipOrg.find({
|
||||||
inviteEmail: email,
|
inviteEmail: email,
|
||||||
status: INVITED
|
status: INVITED,
|
||||||
});
|
});
|
||||||
|
|
||||||
membershipsToUpdate.forEach(async (membership) => {
|
membershipsToUpdate.forEach(async (membership) => {
|
||||||
await updateSubscriptionOrgQuantity({
|
await updateSubscriptionOrgQuantity({
|
||||||
organizationId: membership.organization.toString()
|
organizationId: membership.organization.toString(),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -104,11 +104,11 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
|||||||
await MembershipOrg.updateMany(
|
await MembershipOrg.updateMany(
|
||||||
{
|
{
|
||||||
inviteEmail: email,
|
inviteEmail: email,
|
||||||
status: INVITED
|
status: INVITED,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
user,
|
user,
|
||||||
status: ACCEPTED
|
status: ACCEPTED,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
|||||||
const tokens = await issueAuthTokens({
|
const tokens = await issueAuthTokens({
|
||||||
userId: user._id,
|
userId: user._id,
|
||||||
ip: req.realIP,
|
ip: req.realIP,
|
||||||
userAgent: req.headers['user-agent'] ?? ''
|
userAgent: req.headers["user-agent"] ?? "",
|
||||||
});
|
});
|
||||||
|
|
||||||
token = tokens.token;
|
token = tokens.token;
|
||||||
@@ -127,27 +127,27 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
|||||||
"email": email,
|
"email": email,
|
||||||
"eventName": "Sign Up",
|
"eventName": "Sign Up",
|
||||||
"firstName": firstName,
|
"firstName": firstName,
|
||||||
"lastName": lastName
|
"lastName": lastName,
|
||||||
}, {
|
}, {
|
||||||
headers: {
|
headers: {
|
||||||
"Accept": "application/json",
|
"Accept": "application/json",
|
||||||
"Authorization": "Bearer " + (await getLoopsApiKey())
|
"Authorization": "Bearer " + (await getLoopsApiKey()),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// store (refresh) token in httpOnly cookie
|
// store (refresh) token in httpOnly cookie
|
||||||
res.cookie('jid', tokens.refreshToken, {
|
res.cookie("jid", tokens.refreshToken, {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
path: '/',
|
path: "/",
|
||||||
sameSite: 'strict',
|
sameSite: "strict",
|
||||||
secure: await getHttpsEnabled()
|
secure: await getHttpsEnabled(),
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfully set up account',
|
message: "Successfully set up account",
|
||||||
user,
|
user,
|
||||||
token
|
token,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -172,7 +172,7 @@ export const completeAccountInvite = async (req: Request, res: Response) => {
|
|||||||
encryptedPrivateKeyIV,
|
encryptedPrivateKeyIV,
|
||||||
encryptedPrivateKeyTag,
|
encryptedPrivateKeyTag,
|
||||||
salt,
|
salt,
|
||||||
verifier
|
verifier,
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
// get user
|
// get user
|
||||||
@@ -182,16 +182,16 @@ export const completeAccountInvite = async (req: Request, res: Response) => {
|
|||||||
// case 1: user doesn't exist.
|
// case 1: user doesn't exist.
|
||||||
// case 2: user has already completed account
|
// case 2: user has already completed account
|
||||||
return res.status(403).send({
|
return res.status(403).send({
|
||||||
error: 'Failed to complete account for complete user'
|
error: "Failed to complete account for complete user",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const membershipOrg = await MembershipOrg.findOne({
|
const membershipOrg = await MembershipOrg.findOne({
|
||||||
inviteEmail: email,
|
inviteEmail: email,
|
||||||
status: INVITED
|
status: INVITED,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!membershipOrg) throw new Error('Failed to find invitations for email');
|
if (!membershipOrg) throw new Error("Failed to find invitations for email");
|
||||||
|
|
||||||
// complete setting up user's account
|
// complete setting up user's account
|
||||||
user = await completeAccount({
|
user = await completeAccount({
|
||||||
@@ -207,33 +207,33 @@ export const completeAccountInvite = async (req: Request, res: Response) => {
|
|||||||
encryptedPrivateKeyIV,
|
encryptedPrivateKeyIV,
|
||||||
encryptedPrivateKeyTag,
|
encryptedPrivateKeyTag,
|
||||||
salt,
|
salt,
|
||||||
verifier
|
verifier,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user)
|
if (!user)
|
||||||
throw new Error('Failed to complete account for non-existent user');
|
throw new Error("Failed to complete account for non-existent user");
|
||||||
|
|
||||||
// update organization membership statuses that are
|
// update organization membership statuses that are
|
||||||
// invited to completed with user attached
|
// invited to completed with user attached
|
||||||
const membershipsToUpdate = await MembershipOrg.find({
|
const membershipsToUpdate = await MembershipOrg.find({
|
||||||
inviteEmail: email,
|
inviteEmail: email,
|
||||||
status: INVITED
|
status: INVITED,
|
||||||
});
|
});
|
||||||
|
|
||||||
membershipsToUpdate.forEach(async (membership) => {
|
membershipsToUpdate.forEach(async (membership) => {
|
||||||
await updateSubscriptionOrgQuantity({
|
await updateSubscriptionOrgQuantity({
|
||||||
organizationId: membership.organization.toString()
|
organizationId: membership.organization.toString(),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
await MembershipOrg.updateMany(
|
await MembershipOrg.updateMany(
|
||||||
{
|
{
|
||||||
inviteEmail: email,
|
inviteEmail: email,
|
||||||
status: INVITED
|
status: INVITED,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
user,
|
user,
|
||||||
status: ACCEPTED
|
status: ACCEPTED,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -241,22 +241,22 @@ export const completeAccountInvite = async (req: Request, res: Response) => {
|
|||||||
const tokens = await issueAuthTokens({
|
const tokens = await issueAuthTokens({
|
||||||
userId: user._id,
|
userId: user._id,
|
||||||
ip: req.realIP,
|
ip: req.realIP,
|
||||||
userAgent: req.headers['user-agent'] ?? ''
|
userAgent: req.headers["user-agent"] ?? "",
|
||||||
});
|
});
|
||||||
|
|
||||||
token = tokens.token;
|
token = tokens.token;
|
||||||
|
|
||||||
// store (refresh) token in httpOnly cookie
|
// store (refresh) token in httpOnly cookie
|
||||||
res.cookie('jid', tokens.refreshToken, {
|
res.cookie("jid", tokens.refreshToken, {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
path: '/',
|
path: "/",
|
||||||
sameSite: 'strict',
|
sameSite: "strict",
|
||||||
secure: await getHttpsEnabled()
|
secure: await getHttpsEnabled(),
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfully set up account',
|
message: "Successfully set up account",
|
||||||
user,
|
user,
|
||||||
token
|
token,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,71 +1,65 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import { Types } from 'mongoose';
|
import { Types } from "mongoose";
|
||||||
import {
|
import { Membership, Secret } from "../../models";
|
||||||
Membership, Secret,
|
import Tag, { ITag } from "../../models/tag";
|
||||||
} from '../../models';
|
import { Builder } from "builder-pattern";
|
||||||
import Tag, { ITag } from '../../models/tag';
|
import to from "await-to-js";
|
||||||
import { Builder } from "builder-pattern"
|
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
|
||||||
import to from 'await-to-js';
|
import { MongoError } from "mongodb";
|
||||||
import { BadRequestError, UnauthorizedRequestError } from '../../utils/errors';
|
|
||||||
import { MongoError } from 'mongodb';
|
|
||||||
import { userHasWorkspaceAccess } from '../../ee/helpers/checkMembershipPermissions';
|
|
||||||
|
|
||||||
export const createWorkspaceTag = async (req: Request, res: Response) => {
|
export const createWorkspaceTag = async (req: Request, res: Response) => {
|
||||||
const { workspaceId } = req.params
|
const { workspaceId } = req.params;
|
||||||
const { name, slug } = req.body
|
const { name, slug } = req.body;
|
||||||
const sanitizedTagToCreate = Builder<ITag>()
|
const sanitizedTagToCreate = Builder<ITag>()
|
||||||
.name(name)
|
.name(name)
|
||||||
.workspace(new Types.ObjectId(workspaceId))
|
.workspace(new Types.ObjectId(workspaceId))
|
||||||
.slug(slug)
|
.slug(slug)
|
||||||
.user(new Types.ObjectId(req.user._id))
|
.user(new Types.ObjectId(req.user._id))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
const [err, createdTag] = await to(Tag.create(sanitizedTagToCreate))
|
const [err, createdTag] = await to(Tag.create(sanitizedTagToCreate));
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
if ((err as MongoError).code === 11000) {
|
if ((err as MongoError).code === 11000) {
|
||||||
throw BadRequestError({ message: "Tags must be unique in a workspace" })
|
throw BadRequestError({ message: "Tags must be unique in a workspace" });
|
||||||
}
|
}
|
||||||
|
|
||||||
throw err
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json(createdTag)
|
res.json(createdTag);
|
||||||
}
|
};
|
||||||
|
|
||||||
export const deleteWorkspaceTag = async (req: Request, res: Response) => {
|
export const deleteWorkspaceTag = async (req: Request, res: Response) => {
|
||||||
const { tagId } = req.params
|
const { tagId } = req.params;
|
||||||
|
|
||||||
const tagFromDB = await Tag.findById(tagId)
|
const tagFromDB = await Tag.findById(tagId);
|
||||||
if (!tagFromDB) {
|
if (!tagFromDB) {
|
||||||
throw BadRequestError()
|
throw BadRequestError();
|
||||||
}
|
}
|
||||||
|
|
||||||
// can only delete if the request user is one that belongs to the same workspace as the tag
|
// can only delete if the request user is one that belongs to the same workspace as the tag
|
||||||
const membership = await Membership.findOne({
|
const membership = await Membership.findOne({
|
||||||
user: req.user,
|
user: req.user,
|
||||||
workspace: tagFromDB.workspace
|
workspace: tagFromDB.workspace
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!membership) {
|
if (!membership) {
|
||||||
UnauthorizedRequestError({ message: 'Failed to validate membership' });
|
UnauthorizedRequestError({ message: "Failed to validate membership" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await Tag.findByIdAndDelete(tagId);
|
const result = await Tag.findByIdAndDelete(tagId);
|
||||||
|
|
||||||
// remove the tag from secrets
|
// remove the tag from secrets
|
||||||
await Secret.updateMany(
|
await Secret.updateMany({ tags: { $in: [tagId] } }, { $pull: { tags: tagId } });
|
||||||
{ tags: { $in: [tagId] } },
|
|
||||||
{ $pull: { tags: tagId } }
|
|
||||||
);
|
|
||||||
|
|
||||||
res.json(result);
|
res.json(result);
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getWorkspaceTags = async (req: Request, res: Response) => {
|
export const getWorkspaceTags = async (req: Request, res: Response) => {
|
||||||
const { workspaceId } = req.params
|
const { workspaceId } = req.params;
|
||||||
const workspaceTags = await Tag.find({ workspace: workspaceId })
|
const workspaceTags = await Tag.find({ workspace: workspaceId });
|
||||||
return res.json({
|
return res.json({
|
||||||
workspaceTags
|
workspaceTags
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import {
|
import {
|
||||||
|
MembershipOrg,
|
||||||
User,
|
User,
|
||||||
MembershipOrg
|
} from "../../models";
|
||||||
} from '../../models';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the current user.
|
* Return the current user.
|
||||||
@@ -38,10 +38,10 @@ export const getMe = async (req: Request, res: Response) => {
|
|||||||
*/
|
*/
|
||||||
const user = await User
|
const user = await User
|
||||||
.findById(req.user._id)
|
.findById(req.user._id)
|
||||||
.select('+salt +publicKey +encryptedPrivateKey +iv +tag +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag');
|
.select("+salt +publicKey +encryptedPrivateKey +iv +tag +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag");
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
user
|
user,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ export const updateMyMfaEnabled = async (req: Request, res: Response) => {
|
|||||||
if (isMfaEnabled) {
|
if (isMfaEnabled) {
|
||||||
// TODO: adapt this route/controller
|
// TODO: adapt this route/controller
|
||||||
// to work for different forms of MFA
|
// to work for different forms of MFA
|
||||||
req.user.mfaMethods = ['email'];
|
req.user.mfaMethods = ["email"];
|
||||||
} else {
|
} else {
|
||||||
req.user.mfaMethods = [];
|
req.user.mfaMethods = [];
|
||||||
}
|
}
|
||||||
@@ -70,7 +70,7 @@ export const updateMyMfaEnabled = async (req: Request, res: Response) => {
|
|||||||
const user = req.user;
|
const user = req.user;
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
user
|
user,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,11 +109,11 @@ export const getMyOrganizations = async (req: Request, res: Response) => {
|
|||||||
*/
|
*/
|
||||||
const organizations = (
|
const organizations = (
|
||||||
await MembershipOrg.find({
|
await MembershipOrg.find({
|
||||||
user: req.user._id
|
user: req.user._id,
|
||||||
}).populate('organization')
|
}).populate("organization")
|
||||||
).map((m) => m.organization);
|
).map((m) => m.organization);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
organizations
|
organizations,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,19 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import { Types } from 'mongoose';
|
import { Types } from "mongoose";
|
||||||
import {
|
import {
|
||||||
Workspace,
|
|
||||||
Secret,
|
|
||||||
Membership,
|
|
||||||
MembershipOrg,
|
|
||||||
Integration,
|
|
||||||
IntegrationAuth,
|
|
||||||
Key,
|
Key,
|
||||||
IUser,
|
Membership,
|
||||||
ServiceToken,
|
ServiceTokenData,
|
||||||
ServiceTokenData
|
Workspace,
|
||||||
} from '../../models';
|
} from "../../models";
|
||||||
import {
|
import {
|
||||||
v2PushSecrets as push,
|
|
||||||
pullSecrets as pull,
|
pullSecrets as pull,
|
||||||
reformatPullSecrets
|
v2PushSecrets as push,
|
||||||
} from '../../helpers/secret';
|
reformatPullSecrets,
|
||||||
import { pushKeys } from '../../helpers/key';
|
} from "../../helpers/secret";
|
||||||
import { TelemetryService, EventService } from '../../services';
|
import { pushKeys } from "../../helpers/key";
|
||||||
import { eventPushSecrets } from '../../events';
|
import { EventService, TelemetryService } from "../../services";
|
||||||
|
import { eventPushSecrets } from "../../events";
|
||||||
|
|
||||||
interface V2PushSecret {
|
interface V2PushSecret {
|
||||||
type: string; // personal or shared
|
type: string; // personal or shared
|
||||||
@@ -54,12 +48,12 @@ export const pushWorkspaceSecrets = async (req: Request, res: Response) => {
|
|||||||
// validate environment
|
// validate environment
|
||||||
const workspaceEnvs = req.membership.workspace.environments;
|
const workspaceEnvs = req.membership.workspace.environments;
|
||||||
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
|
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
|
||||||
throw new Error('Failed to validate environment');
|
throw new Error("Failed to validate environment");
|
||||||
}
|
}
|
||||||
|
|
||||||
// sanitize secrets
|
// sanitize secrets
|
||||||
secrets = secrets.filter(
|
secrets = secrets.filter(
|
||||||
(s: V2PushSecret) => s.secretKeyCiphertext !== '' && s.secretValueCiphertext !== ''
|
(s: V2PushSecret) => s.secretKeyCiphertext !== "" && s.secretValueCiphertext !== ""
|
||||||
);
|
);
|
||||||
|
|
||||||
await push({
|
await push({
|
||||||
@@ -67,26 +61,26 @@ export const pushWorkspaceSecrets = async (req: Request, res: Response) => {
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
environment,
|
environment,
|
||||||
secrets,
|
secrets,
|
||||||
channel: channel ? channel : 'cli',
|
channel: channel ? channel : "cli",
|
||||||
ipAddress: req.realIP
|
ipAddress: req.realIP,
|
||||||
});
|
});
|
||||||
|
|
||||||
await pushKeys({
|
await pushKeys({
|
||||||
userId: req.user._id,
|
userId: req.user._id,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
keys
|
keys,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: 'secrets pushed',
|
event: "secrets pushed",
|
||||||
distinctId: req.user.email,
|
distinctId: req.user.email,
|
||||||
properties: {
|
properties: {
|
||||||
numberOfSecrets: secrets.length,
|
numberOfSecrets: secrets.length,
|
||||||
environment,
|
environment,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
channel: channel ? channel : 'cli'
|
channel: channel ? channel : "cli",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,12 +88,12 @@ export const pushWorkspaceSecrets = async (req: Request, res: Response) => {
|
|||||||
EventService.handleEvent({
|
EventService.handleEvent({
|
||||||
event: eventPushSecrets({
|
event: eventPushSecrets({
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment
|
environment,
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfully uploaded workspace secrets'
|
message: "Successfully uploaded workspace secrets",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -126,18 +120,18 @@ export const pullSecrets = async (req: Request, res: Response) => {
|
|||||||
// validate environment
|
// validate environment
|
||||||
const workspaceEnvs = req.membership.workspace.environments;
|
const workspaceEnvs = req.membership.workspace.environments;
|
||||||
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
|
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
|
||||||
throw new Error('Failed to validate environment');
|
throw new Error("Failed to validate environment");
|
||||||
}
|
}
|
||||||
|
|
||||||
secrets = await pull({
|
secrets = await pull({
|
||||||
userId,
|
userId,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
environment,
|
environment,
|
||||||
channel: channel ? channel : 'cli',
|
channel: channel ? channel : "cli",
|
||||||
ipAddress: req.realIP
|
ipAddress: req.realIP,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (channel !== 'cli') {
|
if (channel !== "cli") {
|
||||||
secrets = reformatPullSecrets({ secrets });
|
secrets = reformatPullSecrets({ secrets });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,18 +139,18 @@ export const pullSecrets = async (req: Request, res: Response) => {
|
|||||||
// capture secrets pushed event in production
|
// capture secrets pushed event in production
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
distinctId: req.user.email,
|
distinctId: req.user.email,
|
||||||
event: 'secrets pulled',
|
event: "secrets pulled",
|
||||||
properties: {
|
properties: {
|
||||||
numberOfSecrets: secrets.length,
|
numberOfSecrets: secrets.length,
|
||||||
environment,
|
environment,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
channel: channel ? channel : 'cli'
|
channel: channel ? channel : "cli",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
secrets
|
secrets,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -194,10 +188,10 @@ export const getWorkspaceKey = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
key = await Key.findOne({
|
key = await Key.findOne({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
receiver: req.user._id
|
receiver: req.user._id,
|
||||||
}).populate('sender', '+publicKey');
|
}).populate("sender", "+publicKey");
|
||||||
|
|
||||||
if (!key) throw new Error('Failed to find workspace key');
|
if (!key) throw new Error("Failed to find workspace key");
|
||||||
|
|
||||||
return res.status(200).json(key);
|
return res.status(200).json(key);
|
||||||
}
|
}
|
||||||
@@ -209,12 +203,12 @@ export const getWorkspaceServiceTokenData = async (
|
|||||||
|
|
||||||
const serviceTokenData = await ServiceTokenData
|
const serviceTokenData = await ServiceTokenData
|
||||||
.find({
|
.find({
|
||||||
workspace: workspaceId
|
workspace: workspaceId,
|
||||||
})
|
})
|
||||||
.select('+encryptedKey +iv +tag');
|
.select("+encryptedKey +iv +tag");
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
serviceTokenData
|
serviceTokenData,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,11 +255,11 @@ export const getWorkspaceMemberships = async (req: Request, res: Response) => {
|
|||||||
const { workspaceId } = req.params;
|
const { workspaceId } = req.params;
|
||||||
|
|
||||||
const memberships = await Membership.find({
|
const memberships = await Membership.find({
|
||||||
workspace: workspaceId
|
workspace: workspaceId,
|
||||||
}).populate('user', '+publicKey');
|
}).populate("user", "+publicKey");
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
memberships
|
memberships,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,21 +324,21 @@ export const updateWorkspaceMembership = async (req: Request, res: Response) =>
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
const {
|
const {
|
||||||
membershipId
|
membershipId,
|
||||||
} = req.params;
|
} = req.params;
|
||||||
const { role } = req.body;
|
const { role } = req.body;
|
||||||
|
|
||||||
const membership = await Membership.findByIdAndUpdate(
|
const membership = await Membership.findByIdAndUpdate(
|
||||||
membershipId,
|
membershipId,
|
||||||
{
|
{
|
||||||
role
|
role,
|
||||||
}, {
|
}, {
|
||||||
new: true
|
new: true,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
membership
|
membership,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,20 +386,20 @@ export const deleteWorkspaceMembership = async (req: Request, res: Response) =>
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
const {
|
const {
|
||||||
membershipId
|
membershipId,
|
||||||
} = req.params;
|
} = req.params;
|
||||||
|
|
||||||
const membership = await Membership.findByIdAndDelete(membershipId);
|
const membership = await Membership.findByIdAndDelete(membershipId);
|
||||||
|
|
||||||
if (!membership) throw new Error('Failed to delete workspace membership');
|
if (!membership) throw new Error("Failed to delete workspace membership");
|
||||||
|
|
||||||
await Key.deleteMany({
|
await Key.deleteMany({
|
||||||
receiver: membership.user,
|
receiver: membership.user,
|
||||||
workspace: membership.workspace
|
workspace: membership.workspace,
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
membership
|
membership,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,18 +415,18 @@ export const toggleAutoCapitalization = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
const workspace = await Workspace.findOneAndUpdate(
|
const workspace = await Workspace.findOneAndUpdate(
|
||||||
{
|
{
|
||||||
_id: workspaceId
|
_id: workspaceId,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
autoCapitalization
|
autoCapitalization,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
new: true
|
new: true,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfully changed autoCapitalization setting',
|
message: "Successfully changed autoCapitalization setting",
|
||||||
workspace
|
workspace,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from "jsonwebtoken";
|
||||||
import * as Sentry from '@sentry/node';
|
import * as Sentry from "@sentry/node";
|
||||||
import * as bigintConversion from 'bigint-conversion';
|
import * as bigintConversion from "bigint-conversion";
|
||||||
const jsrp = require('jsrp');
|
const jsrp = require("jsrp");
|
||||||
import { User, LoginSRPDetail } from '../../models';
|
import { LoginSRPDetail, User } from "../../models";
|
||||||
import { issueAuthTokens, createToken, validateProviderAuthToken } from '../../helpers/auth';
|
import { createToken, issueAuthTokens, validateProviderAuthToken } from "../../helpers/auth";
|
||||||
import { checkUserDevice } from '../../helpers/user';
|
import { checkUserDevice } from "../../helpers/user";
|
||||||
import { sendMail } from '../../helpers/nodemailer';
|
import { sendMail } from "../../helpers/nodemailer";
|
||||||
import { TokenService } from '../../services';
|
import { TokenService } from "../../services";
|
||||||
import { EELogService } from '../../ee/services';
|
import { EELogService } from "../../ee/services";
|
||||||
import { BadRequestError, InternalServerError } from '../../utils/errors';
|
import { BadRequestError, InternalServerError } from "../../utils/errors";
|
||||||
import {
|
import {
|
||||||
|
ACTION_LOGIN,
|
||||||
TOKEN_EMAIL_MFA,
|
TOKEN_EMAIL_MFA,
|
||||||
ACTION_LOGIN
|
} from "../../variables";
|
||||||
} from '../../variables';
|
import { getChannelFromUserAgent } from "../../utils/posthog"; // TODO: move this
|
||||||
import { getChannelFromUserAgent } from '../../utils/posthog'; // TODO: move this
|
|
||||||
import {
|
import {
|
||||||
|
getHttpsEnabled,
|
||||||
getJwtMfaLifetime,
|
getJwtMfaLifetime,
|
||||||
getJwtMfaSecret,
|
getJwtMfaSecret,
|
||||||
getHttpsEnabled,
|
} from "../../config";
|
||||||
} from '../../config';
|
import { AuthProvider } from "../../models/user";
|
||||||
import { AuthProvider } from '../../models/user';
|
|
||||||
|
|
||||||
declare module 'jsonwebtoken' {
|
declare module "jsonwebtoken" {
|
||||||
export interface ProviderAuthJwtPayload extends jwt.JwtPayload {
|
export interface ProviderAuthJwtPayload extends jwt.JwtPayload {
|
||||||
userId: string;
|
userId: string;
|
||||||
email: string;
|
email: string;
|
||||||
@@ -43,7 +43,7 @@ export const login1 = async (req: Request, res: Response) => {
|
|||||||
const {
|
const {
|
||||||
email,
|
email,
|
||||||
providerAuthToken,
|
providerAuthToken,
|
||||||
clientPublicKey
|
clientPublicKey,
|
||||||
}: {
|
}: {
|
||||||
email: string;
|
email: string;
|
||||||
clientPublicKey: string,
|
clientPublicKey: string,
|
||||||
@@ -52,9 +52,9 @@ export const login1 = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
email,
|
email,
|
||||||
}).select('+salt +verifier');
|
}).select("+salt +verifier");
|
||||||
|
|
||||||
if (!user) throw new Error('Failed to find user');
|
if (!user) throw new Error("Failed to find user");
|
||||||
|
|
||||||
if (user.authProvider) {
|
if (user.authProvider) {
|
||||||
await validateProviderAuthToken({
|
await validateProviderAuthToken({
|
||||||
@@ -68,13 +68,13 @@ export const login1 = async (req: Request, res: Response) => {
|
|||||||
server.init(
|
server.init(
|
||||||
{
|
{
|
||||||
salt: user.salt,
|
salt: user.salt,
|
||||||
verifier: user.verifier
|
verifier: user.verifier,
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
// generate server-side public key
|
// generate server-side public key
|
||||||
const serverPublicKey = server.getPublicKey();
|
const serverPublicKey = server.getPublicKey();
|
||||||
await LoginSRPDetail.findOneAndReplace({
|
await LoginSRPDetail.findOneAndReplace({
|
||||||
email: email
|
email: email,
|
||||||
}, {
|
}, {
|
||||||
email,
|
email,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
@@ -84,7 +84,7 @@ export const login1 = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
serverPublicKey,
|
serverPublicKey,
|
||||||
salt: user.salt
|
salt: user.salt,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -92,7 +92,7 @@ export const login1 = async (req: Request, res: Response) => {
|
|||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
message: 'Failed to start authentication process'
|
message: "Failed to start authentication process",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -107,15 +107,15 @@ export const login1 = async (req: Request, res: Response) => {
|
|||||||
export const login2 = async (req: Request, res: Response) => {
|
export const login2 = async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
if (!req.headers['user-agent']) throw InternalServerError({ message: 'User-Agent header is required' });
|
if (!req.headers["user-agent"]) throw InternalServerError({ message: "User-Agent header is required" });
|
||||||
|
|
||||||
const { email, clientProof, providerAuthToken } = req.body;
|
const { email, clientProof, providerAuthToken } = req.body;
|
||||||
|
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
email,
|
email,
|
||||||
}).select('+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag +devices');
|
}).select("+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag +devices");
|
||||||
|
|
||||||
if (!user) throw new Error('Failed to find user');
|
if (!user) throw new Error("Failed to find user");
|
||||||
|
|
||||||
if (user.authProvider) {
|
if (user.authProvider) {
|
||||||
await validateProviderAuthToken({
|
await validateProviderAuthToken({
|
||||||
@@ -136,7 +136,7 @@ export const login2 = async (req: Request, res: Response) => {
|
|||||||
{
|
{
|
||||||
salt: user.salt,
|
salt: user.salt,
|
||||||
verifier: user.verifier,
|
verifier: user.verifier,
|
||||||
b: loginSRPDetail.serverBInt
|
b: loginSRPDetail.serverBInt,
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
server.setClientPublicKey(loginSRPDetail.clientPublicKey);
|
server.setClientPublicKey(loginSRPDetail.clientPublicKey);
|
||||||
@@ -150,52 +150,52 @@ export const login2 = async (req: Request, res: Response) => {
|
|||||||
// generate temporary MFA token
|
// generate temporary MFA token
|
||||||
const token = createToken({
|
const token = createToken({
|
||||||
payload: {
|
payload: {
|
||||||
userId: user._id.toString()
|
userId: user._id.toString(),
|
||||||
},
|
},
|
||||||
expiresIn: await getJwtMfaLifetime(),
|
expiresIn: await getJwtMfaLifetime(),
|
||||||
secret: await getJwtMfaSecret()
|
secret: await getJwtMfaSecret(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const code = await TokenService.createToken({
|
const code = await TokenService.createToken({
|
||||||
type: TOKEN_EMAIL_MFA,
|
type: TOKEN_EMAIL_MFA,
|
||||||
email
|
email,
|
||||||
});
|
});
|
||||||
|
|
||||||
// send MFA code [code] to [email]
|
// send MFA code [code] to [email]
|
||||||
await sendMail({
|
await sendMail({
|
||||||
template: 'emailMfa.handlebars',
|
template: "emailMfa.handlebars",
|
||||||
subjectLine: 'Infisical MFA code',
|
subjectLine: "Infisical MFA code",
|
||||||
recipients: [user.email],
|
recipients: [user.email],
|
||||||
substitutions: {
|
substitutions: {
|
||||||
code
|
code,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
mfaEnabled: true,
|
mfaEnabled: true,
|
||||||
token
|
token,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await checkUserDevice({
|
await checkUserDevice({
|
||||||
user,
|
user,
|
||||||
ip: req.realIP,
|
ip: req.realIP,
|
||||||
userAgent: req.headers['user-agent'] ?? ''
|
userAgent: req.headers["user-agent"] ?? "",
|
||||||
});
|
});
|
||||||
|
|
||||||
// issue tokens
|
// issue tokens
|
||||||
const tokens = await issueAuthTokens({
|
const tokens = await issueAuthTokens({
|
||||||
userId: user._id,
|
userId: user._id,
|
||||||
ip: req.realIP,
|
ip: req.realIP,
|
||||||
userAgent: req.headers['user-agent'] ?? ''
|
userAgent: req.headers["user-agent"] ?? "",
|
||||||
});
|
});
|
||||||
|
|
||||||
// store (refresh) token in httpOnly cookie
|
// store (refresh) token in httpOnly cookie
|
||||||
res.cookie('jid', tokens.refreshToken, {
|
res.cookie("jid", tokens.refreshToken, {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
path: '/',
|
path: "/",
|
||||||
sameSite: 'strict',
|
sameSite: "strict",
|
||||||
secure: await getHttpsEnabled()
|
secure: await getHttpsEnabled(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// case: user does not have MFA enablgged
|
// case: user does not have MFA enablgged
|
||||||
@@ -221,7 +221,7 @@ export const login2 = async (req: Request, res: Response) => {
|
|||||||
publicKey: user.publicKey,
|
publicKey: user.publicKey,
|
||||||
encryptedPrivateKey: user.encryptedPrivateKey,
|
encryptedPrivateKey: user.encryptedPrivateKey,
|
||||||
iv: user.iv,
|
iv: user.iv,
|
||||||
tag: user.tag
|
tag: user.tag,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -236,21 +236,21 @@ export const login2 = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
const loginAction = await EELogService.createAction({
|
const loginAction = await EELogService.createAction({
|
||||||
name: ACTION_LOGIN,
|
name: ACTION_LOGIN,
|
||||||
userId: user._id
|
userId: user._id,
|
||||||
});
|
});
|
||||||
|
|
||||||
loginAction && await EELogService.createLog({
|
loginAction && await EELogService.createLog({
|
||||||
userId: user._id,
|
userId: user._id,
|
||||||
actions: [loginAction],
|
actions: [loginAction],
|
||||||
channel: getChannelFromUserAgent(req.headers['user-agent']),
|
channel: getChannelFromUserAgent(req.headers["user-agent"]),
|
||||||
ipAddress: req.realIP
|
ipAddress: req.realIP,
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send(response);
|
return res.status(200).send(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
message: 'Failed to authenticate. Try again?'
|
message: "Failed to authenticate. Try again?",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -258,7 +258,7 @@ export const login2 = async (req: Request, res: Response) => {
|
|||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
message: 'Failed to authenticate. Try again?'
|
message: "Failed to authenticate. Try again?",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as secretsController from './secretsController';
|
import * as secretsController from "./secretsController";
|
||||||
import * as workspacesController from './workspacesController';
|
import * as workspacesController from "./workspacesController";
|
||||||
import * as authController from './authController';
|
import * as authController from "./authController";
|
||||||
import * as signupController from './signupController';
|
import * as signupController from "./signupController";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
authController,
|
authController,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { SecretService, EventService } from "../../services";
|
import { EventService, SecretService } from "../../services";
|
||||||
import { eventPushSecrets } from "../../events";
|
import { eventPushSecrets } from "../../events";
|
||||||
import { BotService } from "../../services";
|
import { BotService } from "../../services";
|
||||||
import { repackageSecretToRaw } from "../../helpers/secrets";
|
import { repackageSecretToRaw } from "../../helpers/secrets";
|
||||||
@@ -25,18 +25,18 @@ export const getSecretsRaw = async (req: Request, res: Response) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const key = await BotService.getWorkspaceKeyWithBot({
|
const key = await BotService.getWorkspaceKeyWithBot({
|
||||||
workspaceId: new Types.ObjectId(workspaceId)
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
secrets: secrets.map((secret) => {
|
secrets: secrets.map((secret) => {
|
||||||
const rep = repackageSecretToRaw({
|
const rep = repackageSecretToRaw({
|
||||||
secret,
|
secret,
|
||||||
key
|
key,
|
||||||
});
|
});
|
||||||
|
|
||||||
return rep;
|
return rep;
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -62,14 +62,14 @@ export const getSecretByNameRaw = async (req: Request, res: Response) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const key = await BotService.getWorkspaceKeyWithBot({
|
const key = await BotService.getWorkspaceKeyWithBot({
|
||||||
workspaceId: new Types.ObjectId(workspaceId)
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
secret: repackageSecretToRaw({
|
secret: repackageSecretToRaw({
|
||||||
secret,
|
secret,
|
||||||
key
|
key,
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -86,26 +86,26 @@ export const createSecretRaw = async (req: Request, res: Response) => {
|
|||||||
type,
|
type,
|
||||||
secretValue,
|
secretValue,
|
||||||
secretComment,
|
secretComment,
|
||||||
secretPath = "/"
|
secretPath = "/",
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
const key = await BotService.getWorkspaceKeyWithBot({
|
const key = await BotService.getWorkspaceKeyWithBot({
|
||||||
workspaceId: new Types.ObjectId(workspaceId)
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8({
|
const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8({
|
||||||
plaintext: secretName,
|
plaintext: secretName,
|
||||||
key
|
key,
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8({
|
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8({
|
||||||
plaintext: secretValue,
|
plaintext: secretValue,
|
||||||
key
|
key,
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8({
|
const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8({
|
||||||
plaintext: secretComment,
|
plaintext: secretComment,
|
||||||
key
|
key,
|
||||||
});
|
});
|
||||||
|
|
||||||
const secret = await SecretService.createSecret({
|
const secret = await SecretService.createSecret({
|
||||||
@@ -123,7 +123,7 @@ export const createSecretRaw = async (req: Request, res: Response) => {
|
|||||||
secretPath,
|
secretPath,
|
||||||
secretCommentCiphertext: secretCommentEncrypted.ciphertext,
|
secretCommentCiphertext: secretCommentEncrypted.ciphertext,
|
||||||
secretCommentIV: secretCommentEncrypted.iv,
|
secretCommentIV: secretCommentEncrypted.iv,
|
||||||
secretCommentTag: secretCommentEncrypted.tag
|
secretCommentTag: secretCommentEncrypted.tag,
|
||||||
});
|
});
|
||||||
|
|
||||||
await EventService.handleEvent({
|
await EventService.handleEvent({
|
||||||
@@ -139,8 +139,8 @@ export const createSecretRaw = async (req: Request, res: Response) => {
|
|||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
secret: repackageSecretToRaw({
|
secret: repackageSecretToRaw({
|
||||||
secret: secretWithoutBlindIndex,
|
secret: secretWithoutBlindIndex,
|
||||||
key
|
key,
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,12 +160,12 @@ export const updateSecretByNameRaw = async (req: Request, res: Response) => {
|
|||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
const key = await BotService.getWorkspaceKeyWithBot({
|
const key = await BotService.getWorkspaceKeyWithBot({
|
||||||
workspaceId: new Types.ObjectId(workspaceId)
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8({
|
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8({
|
||||||
plaintext: secretValue,
|
plaintext: secretValue,
|
||||||
key
|
key,
|
||||||
});
|
});
|
||||||
|
|
||||||
const secret = await SecretService.updateSecret({
|
const secret = await SecretService.updateSecret({
|
||||||
@@ -190,8 +190,8 @@ export const updateSecretByNameRaw = async (req: Request, res: Response) => {
|
|||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
secret: repackageSecretToRaw({
|
secret: repackageSecretToRaw({
|
||||||
secret,
|
secret,
|
||||||
key
|
key,
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -206,7 +206,7 @@ export const deleteSecretByNameRaw = async (req: Request, res: Response) => {
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
environment,
|
environment,
|
||||||
type,
|
type,
|
||||||
secretPath = "/"
|
secretPath = "/",
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
const { secret } = await SecretService.deleteSecret({
|
const { secret } = await SecretService.deleteSecret({
|
||||||
@@ -226,14 +226,14 @@ export const deleteSecretByNameRaw = async (req: Request, res: Response) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const key = await BotService.getWorkspaceKeyWithBot({
|
const key = await BotService.getWorkspaceKeyWithBot({
|
||||||
workspaceId: new Types.ObjectId(workspaceId)
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
secret: repackageSecretToRaw({
|
secret: repackageSecretToRaw({
|
||||||
secret,
|
secret,
|
||||||
key
|
key,
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -324,7 +324,7 @@ export const createSecret = async (req: Request, res: Response) => {
|
|||||||
secretPath,
|
secretPath,
|
||||||
secretCommentCiphertext,
|
secretCommentCiphertext,
|
||||||
secretCommentIV,
|
secretCommentIV,
|
||||||
secretCommentTag
|
secretCommentTag,
|
||||||
});
|
});
|
||||||
|
|
||||||
await EventService.handleEvent({
|
await EventService.handleEvent({
|
||||||
@@ -395,7 +395,7 @@ export const deleteSecretByName = async (req: Request, res: Response) => {
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
environment,
|
environment,
|
||||||
type,
|
type,
|
||||||
secretPath = "/"
|
secretPath = "/",
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
const { secret } = await SecretService.deleteSecret({
|
const { secret } = await SecretService.deleteSecret({
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import jwt from 'jsonwebtoken';
|
import jwt from "jsonwebtoken";
|
||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import * as Sentry from '@sentry/node';
|
import * as Sentry from "@sentry/node";
|
||||||
import { User, MembershipOrg } from '../../models';
|
import { MembershipOrg, User } from "../../models";
|
||||||
import { completeAccount } from '../../helpers/user';
|
import { completeAccount } from "../../helpers/user";
|
||||||
import {
|
import {
|
||||||
initializeDefaultOrg
|
initializeDefaultOrg,
|
||||||
} from '../../helpers/signup';
|
} from "../../helpers/signup";
|
||||||
import { issueAuthTokens, validateProviderAuthToken } from '../../helpers/auth';
|
import { issueAuthTokens, validateProviderAuthToken } from "../../helpers/auth";
|
||||||
import { INVITED, ACCEPTED } from '../../variables';
|
import { ACCEPTED, INVITED } from "../../variables";
|
||||||
import { standardRequest } from '../../config/request';
|
import { standardRequest } from "../../config/request";
|
||||||
import { getLoopsApiKey, getHttpsEnabled, getJwtSignupSecret } from '../../config';
|
import { getHttpsEnabled, getJwtSignupSecret, getLoopsApiKey } from "../../config";
|
||||||
import { BadRequestError } from '../../utils/errors';
|
import { BadRequestError } from "../../utils/errors";
|
||||||
import { TelemetryService } from '../../services';
|
import { TelemetryService } from "../../services";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Complete setting up user by adding their personal and auth information as part of the
|
* Complete setting up user by adding their personal and auth information as part of the
|
||||||
@@ -63,7 +63,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
|||||||
// case 1: user doesn't exist.
|
// case 1: user doesn't exist.
|
||||||
// case 2: user has already completed account
|
// case 2: user has already completed account
|
||||||
return res.status(403).send({
|
return res.status(403).send({
|
||||||
error: 'Failed to complete account for complete user'
|
error: "Failed to complete account for complete user",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,16 +74,16 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
|||||||
user,
|
user,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const [AUTH_TOKEN_TYPE, AUTH_TOKEN_VALUE] = <[string, string]>req.headers['authorization']?.split(' ', 2) ?? [null, null]
|
const [AUTH_TOKEN_TYPE, AUTH_TOKEN_VALUE] = <[string, string]>req.headers["authorization"]?.split(" ", 2) ?? [null, null]
|
||||||
if (AUTH_TOKEN_TYPE === null) {
|
if (AUTH_TOKEN_TYPE === null) {
|
||||||
throw BadRequestError({ message: `Missing Authorization Header in the request header.` });
|
throw BadRequestError({ message: "Missing Authorization Header in the request header." });
|
||||||
}
|
}
|
||||||
if (AUTH_TOKEN_TYPE.toLowerCase() !== 'bearer') {
|
if (AUTH_TOKEN_TYPE.toLowerCase() !== "bearer") {
|
||||||
throw BadRequestError({ message: `The provided authentication type '${AUTH_TOKEN_TYPE}' is not supported.` })
|
throw BadRequestError({ message: `The provided authentication type '${AUTH_TOKEN_TYPE}' is not supported.` })
|
||||||
}
|
}
|
||||||
if (AUTH_TOKEN_VALUE === null) {
|
if (AUTH_TOKEN_VALUE === null) {
|
||||||
throw BadRequestError({
|
throw BadRequestError({
|
||||||
message: 'Missing Authorization Body in the request header',
|
message: "Missing Authorization Body in the request header",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,16 +110,16 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
|||||||
encryptedPrivateKeyIV,
|
encryptedPrivateKeyIV,
|
||||||
encryptedPrivateKeyTag,
|
encryptedPrivateKeyTag,
|
||||||
salt,
|
salt,
|
||||||
verifier
|
verifier,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user)
|
if (!user)
|
||||||
throw new Error('Failed to complete account for non-existent user'); // ensure user is non-null
|
throw new Error("Failed to complete account for non-existent user"); // ensure user is non-null
|
||||||
|
|
||||||
// initialize default organization and workspace
|
// initialize default organization and workspace
|
||||||
await initializeDefaultOrg({
|
await initializeDefaultOrg({
|
||||||
organizationName,
|
organizationName,
|
||||||
user
|
user,
|
||||||
});
|
});
|
||||||
|
|
||||||
// update organization membership statuses that are
|
// update organization membership statuses that are
|
||||||
@@ -127,11 +127,11 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
|||||||
await MembershipOrg.updateMany(
|
await MembershipOrg.updateMany(
|
||||||
{
|
{
|
||||||
inviteEmail: email,
|
inviteEmail: email,
|
||||||
status: INVITED
|
status: INVITED,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
user,
|
user,
|
||||||
status: ACCEPTED
|
status: ACCEPTED,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
|||||||
const tokens = await issueAuthTokens({
|
const tokens = await issueAuthTokens({
|
||||||
userId: user._id,
|
userId: user._id,
|
||||||
ip: req.realIP,
|
ip: req.realIP,
|
||||||
userAgent: req.headers['user-agent'] ?? ''
|
userAgent: req.headers["user-agent"] ?? "",
|
||||||
});
|
});
|
||||||
|
|
||||||
token = tokens.token;
|
token = tokens.token;
|
||||||
@@ -150,45 +150,45 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
|||||||
"email": email,
|
"email": email,
|
||||||
"eventName": "Sign Up",
|
"eventName": "Sign Up",
|
||||||
"firstName": firstName,
|
"firstName": firstName,
|
||||||
"lastName": lastName
|
"lastName": lastName,
|
||||||
}, {
|
}, {
|
||||||
headers: {
|
headers: {
|
||||||
"Accept": "application/json",
|
"Accept": "application/json",
|
||||||
"Authorization": "Bearer " + (await getLoopsApiKey())
|
"Authorization": "Bearer " + (await getLoopsApiKey()),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// store (refresh) token in httpOnly cookie
|
// store (refresh) token in httpOnly cookie
|
||||||
res.cookie('jid', tokens.refreshToken, {
|
res.cookie("jid", tokens.refreshToken, {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
path: '/',
|
path: "/",
|
||||||
sameSite: 'strict',
|
sameSite: "strict",
|
||||||
secure: await getHttpsEnabled()
|
secure: await getHttpsEnabled(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const postHogClient = await TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: 'User Signed Up',
|
event: "User Signed Up",
|
||||||
distinctId: email,
|
distinctId: email,
|
||||||
properties: {
|
properties: {
|
||||||
email,
|
email,
|
||||||
attributionSource
|
attributionSource,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
message: 'Failed to complete account setup'
|
message: "Failed to complete account setup",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfully set up account',
|
message: "Successfully set up account",
|
||||||
user,
|
user,
|
||||||
token
|
token,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import { Types } from 'mongoose';
|
import { Types } from "mongoose";
|
||||||
import { Secret } from '../../models';
|
import { Secret } from "../../models";
|
||||||
import { SecretService } from'../../services';
|
import { SecretService } from"../../services";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether or not all secrets in workspace with id [workspaceId]
|
* Return whether or not all secrets in workspace with id [workspaceId]
|
||||||
@@ -16,8 +16,8 @@ export const getWorkspaceBlindIndexStatus = async (req: Request, res: Response)
|
|||||||
const secretsWithoutBlindIndex = await Secret.countDocuments({
|
const secretsWithoutBlindIndex = await Secret.countDocuments({
|
||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
secretBlindIndex: {
|
secretBlindIndex: {
|
||||||
$exists: false
|
$exists: false,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send(secretsWithoutBlindIndex === 0);
|
return res.status(200).send(secretsWithoutBlindIndex === 0);
|
||||||
@@ -30,11 +30,11 @@ export const getWorkspaceSecrets = async (req: Request, res: Response) => {
|
|||||||
const { workspaceId } = req.params;
|
const { workspaceId } = req.params;
|
||||||
|
|
||||||
const secrets = await Secret.find({
|
const secrets = await Secret.find({
|
||||||
workspace: new Types.ObjectId (workspaceId)
|
workspace: new Types.ObjectId (workspaceId),
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
secrets
|
secrets,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,14 +51,14 @@ export const nameWorkspaceSecrets = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
const { workspaceId } = req.params;
|
const { workspaceId } = req.params;
|
||||||
const {
|
const {
|
||||||
secretsToUpdate
|
secretsToUpdate,
|
||||||
}: {
|
}: {
|
||||||
secretsToUpdate: SecretToUpdate[];
|
secretsToUpdate: SecretToUpdate[];
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
// get secret blind index salt
|
// get secret blind index salt
|
||||||
const salt = await SecretService.getSecretBlindIndexSalt({
|
const salt = await SecretService.getSecretBlindIndexSalt({
|
||||||
workspaceId: new Types.ObjectId(workspaceId)
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
});
|
});
|
||||||
|
|
||||||
// update secret blind indices
|
// update secret blind indices
|
||||||
@@ -66,18 +66,18 @@ export const nameWorkspaceSecrets = async (req: Request, res: Response) => {
|
|||||||
secretsToUpdate.map(async (secretToUpdate: SecretToUpdate) => {
|
secretsToUpdate.map(async (secretToUpdate: SecretToUpdate) => {
|
||||||
const secretBlindIndex = await SecretService.generateSecretBlindIndexWithSalt({
|
const secretBlindIndex = await SecretService.generateSecretBlindIndexWithSalt({
|
||||||
secretName: secretToUpdate.secretName,
|
secretName: secretToUpdate.secretName,
|
||||||
salt
|
salt,
|
||||||
});
|
});
|
||||||
|
|
||||||
return ({
|
return ({
|
||||||
updateOne: {
|
updateOne: {
|
||||||
filter: {
|
filter: {
|
||||||
_id: new Types.ObjectId(secretToUpdate._id)
|
_id: new Types.ObjectId(secretToUpdate._id),
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
secretBlindIndex
|
secretBlindIndex,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -85,6 +85,6 @@ export const nameWorkspaceSecrets = async (req: Request, res: Response) => {
|
|||||||
await Secret.bulkWrite(operations);
|
await Secret.bulkWrite(operations);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfully named workspace secrets'
|
message: "Successfully named workspace secrets",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import { Action, SecretVersion } from '../../models';
|
import { Action } from "../../models";
|
||||||
import { ActionNotFoundError } from '../../../utils/errors';
|
import { ActionNotFoundError } from "../../../utils/errors";
|
||||||
|
|
||||||
export const getAction = async (req: Request, res: Response) => {
|
export const getAction = async (req: Request, res: Response) => {
|
||||||
let action;
|
let action;
|
||||||
@@ -10,21 +10,21 @@ export const getAction = async (req: Request, res: Response) => {
|
|||||||
action = await Action
|
action = await Action
|
||||||
.findById(actionId)
|
.findById(actionId)
|
||||||
.populate([
|
.populate([
|
||||||
'payload.secretVersions.oldSecretVersion',
|
"payload.secretVersions.oldSecretVersion",
|
||||||
'payload.secretVersions.newSecretVersion'
|
"payload.secretVersions.newSecretVersion",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!action) throw ActionNotFoundError({
|
if (!action) throw ActionNotFoundError({
|
||||||
message: 'Failed to find action'
|
message: "Failed to find action",
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw ActionNotFoundError({
|
throw ActionNotFoundError({
|
||||||
message: 'Failed to find action'
|
message: "Failed to find action",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
action
|
action,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import { EELicenseService } from '../../services';
|
import { EELicenseService } from "../../services";
|
||||||
import { getLicenseServerUrl } from '../../../config';
|
import { getLicenseServerUrl } from "../../../config";
|
||||||
import { licenseServerKeyRequest } from '../../../config/request';
|
import { licenseServerKeyRequest } from "../../../config/request";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return available cloud product information.
|
* Return available cloud product information.
|
||||||
@@ -11,9 +11,9 @@ import { licenseServerKeyRequest } from '../../../config/request';
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getCloudProducts = async (req: Request, res: Response) => {
|
export const getCloudProducts = async (req: Request, res: Response) => {
|
||||||
const billingCycle = req.query['billing-cycle'] as string;
|
const billingCycle = req.query["billing-cycle"] as string;
|
||||||
|
|
||||||
if (EELicenseService.instanceType === 'cloud') {
|
if (EELicenseService.instanceType === "cloud") {
|
||||||
const { data } = await licenseServerKeyRequest.get(
|
const { data } = await licenseServerKeyRequest.get(
|
||||||
`${await getLicenseServerUrl()}/api/license-server/v1/cloud-products?billing-cycle=${billingCycle}`
|
`${await getLicenseServerUrl()}/api/license-server/v1/cloud-products?billing-cycle=${billingCycle}`
|
||||||
);
|
);
|
||||||
@@ -23,6 +23,6 @@ export const getCloudProducts = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
head: [],
|
head: [],
|
||||||
rows: []
|
rows: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import * as stripeController from './stripeController';
|
import * as stripeController from "./stripeController";
|
||||||
import * as secretController from './secretController';
|
import * as secretController from "./secretController";
|
||||||
import * as secretSnapshotController from './secretSnapshotController';
|
import * as secretSnapshotController from "./secretSnapshotController";
|
||||||
import * as organizationsController from './organizationsController';
|
import * as organizationsController from "./organizationsController";
|
||||||
import * as workspaceController from './workspaceController';
|
import * as workspaceController from "./workspaceController";
|
||||||
import * as actionController from './actionController';
|
import * as actionController from "./actionController";
|
||||||
import * as membershipController from './membershipController';
|
import * as membershipController from "./membershipController";
|
||||||
import * as cloudProductsController from './cloudProductsController';
|
import * as cloudProductsController from "./cloudProductsController";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
stripeController,
|
stripeController,
|
||||||
@@ -15,5 +15,5 @@ export {
|
|||||||
workspaceController,
|
workspaceController,
|
||||||
actionController,
|
actionController,
|
||||||
membershipController,
|
membershipController,
|
||||||
cloudProductsController
|
cloudProductsController,
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ import { Membership, Workspace } from "../../../models";
|
|||||||
import { IMembershipPermission } from "../../../models/membership";
|
import { IMembershipPermission } from "../../../models/membership";
|
||||||
import { BadRequestError, UnauthorizedRequestError } from "../../../utils/errors";
|
import { BadRequestError, UnauthorizedRequestError } from "../../../utils/errors";
|
||||||
import { ADMIN, MEMBER } from "../../../variables/organization";
|
import { ADMIN, MEMBER } from "../../../variables/organization";
|
||||||
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from '../../../variables';
|
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../../../variables";
|
||||||
import { Builder } from "builder-pattern"
|
import { Builder } from "builder-pattern"
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ export const denyMembershipPermissions = async (req: Request, res: Response) =>
|
|||||||
throw BadRequestError({ message: "Something went wrong when locating the related workspace" })
|
throw BadRequestError({ message: "Something went wrong when locating the related workspace" })
|
||||||
}
|
}
|
||||||
|
|
||||||
const uniqueEnvironmentSlugs = new Set(_.uniq(_.map(relatedWorkspace.environments, 'slug')));
|
const uniqueEnvironmentSlugs = new Set(_.uniq(_.map(relatedWorkspace.environments, "slug")));
|
||||||
|
|
||||||
sanitizedMembershipPermissionsUnique.forEach(permission => {
|
sanitizedMembershipPermissionsUnique.forEach(permission => {
|
||||||
if (!uniqueEnvironmentSlugs.has(permission.environmentSlug)) {
|
if (!uniqueEnvironmentSlugs.has(permission.environmentSlug)) {
|
||||||
@@ -59,6 +59,6 @@ export const denyMembershipPermissions = async (req: Request, res: Response) =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
res.send({
|
res.send({
|
||||||
permissionsDenied: updatedMembershipWithPermissions.deniedPermissions
|
permissionsDenied: updatedMembershipWithPermissions.deniedPermissions,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import { getLicenseServerUrl } from '../../../config';
|
import { getLicenseServerUrl } from "../../../config";
|
||||||
import { licenseServerKeyRequest } from '../../../config/request';
|
import { licenseServerKeyRequest } from "../../../config/request";
|
||||||
import { EELicenseService } from '../../services';
|
import { EELicenseService } from "../../services";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the organization's current plan and allowed feature set
|
* Return the organization's current plan and allowed feature set
|
||||||
@@ -25,13 +25,13 @@ export const getOrganizationPlan = async (req: Request, res: Response) => {
|
|||||||
*/
|
*/
|
||||||
export const updateOrganizationPlan = async (req: Request, res: Response) => {
|
export const updateOrganizationPlan = async (req: Request, res: Response) => {
|
||||||
const {
|
const {
|
||||||
productId
|
productId,
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
const { data } = await licenseServerKeyRequest.patch(
|
const { data } = await licenseServerKeyRequest.patch(
|
||||||
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/cloud-plan`,
|
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/cloud-plan`,
|
||||||
{
|
{
|
||||||
productId
|
productId,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ export const getOrganizationPmtMethods = async (req: Request, res: Response) =>
|
|||||||
);
|
);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
pmtMethods
|
pmtMethods,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,19 +57,19 @@ export const getOrganizationPmtMethods = async (req: Request, res: Response) =>
|
|||||||
export const addOrganizationPmtMethod = async (req: Request, res: Response) => {
|
export const addOrganizationPmtMethod = async (req: Request, res: Response) => {
|
||||||
const {
|
const {
|
||||||
success_url,
|
success_url,
|
||||||
cancel_url
|
cancel_url,
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
const { data: { url } } = await licenseServerKeyRequest.post(
|
const { data: { url } } = await licenseServerKeyRequest.post(
|
||||||
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details/payment-methods`,
|
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details/payment-methods`,
|
||||||
{
|
{
|
||||||
success_url,
|
success_url,
|
||||||
cancel_url
|
cancel_url,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
url
|
url,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ export const getSecretSnapshot = async (req: Request, res: Response) => {
|
|||||||
const secretSnapshot = await SecretSnapshot.findById(secretSnapshotId)
|
const secretSnapshot = await SecretSnapshot.findById(secretSnapshotId)
|
||||||
.lean()
|
.lean()
|
||||||
.populate<{ secretVersions: ISecretVersion[] }>({
|
.populate<{ secretVersions: ISecretVersion[] }>({
|
||||||
path: 'secretVersions',
|
path: "secretVersions",
|
||||||
populate: {
|
populate: {
|
||||||
path: 'tags',
|
path: "tags",
|
||||||
model: 'Tag'
|
model: "Tag",
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.populate<{ folderVersion: TFolderRootVersionSchema }>("folderVersion");
|
.populate<{ folderVersion: TFolderRootVersionSchema }>("folderVersion");
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import Stripe from 'stripe';
|
import Stripe from "stripe";
|
||||||
import { getStripeSecretKey, getStripeWebhookSecret } from '../../../config';
|
import { getStripeSecretKey, getStripeWebhookSecret } from "../../../config";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle service provisioning/un-provisioning via Stripe
|
* Handle service provisioning/un-provisioning via Stripe
|
||||||
@@ -10,11 +10,11 @@ import { getStripeSecretKey, getStripeWebhookSecret } from '../../../config';
|
|||||||
*/
|
*/
|
||||||
export const handleWebhook = async (req: Request, res: Response) => {
|
export const handleWebhook = async (req: Request, res: Response) => {
|
||||||
const stripe = new Stripe(await getStripeSecretKey(), {
|
const stripe = new Stripe(await getStripeSecretKey(), {
|
||||||
apiVersion: '2022-08-01'
|
apiVersion: "2022-08-01",
|
||||||
});
|
});
|
||||||
|
|
||||||
// check request for valid stripe signature
|
// check request for valid stripe signature
|
||||||
const sig = req.headers['stripe-signature'] as string;
|
const sig = req.headers["stripe-signature"] as string;
|
||||||
const event = stripe.webhooks.constructEvent(
|
const event = stripe.webhooks.constructEvent(
|
||||||
req.body,
|
req.body,
|
||||||
sig,
|
sig,
|
||||||
@@ -22,7 +22,7 @@ export const handleWebhook = async (req: Request, res: Response) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case '':
|
case "":
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ import { Request, Response } from "express";
|
|||||||
import { PipelineStage, Types } from "mongoose";
|
import { PipelineStage, Types } from "mongoose";
|
||||||
import { Secret } from "../../../models";
|
import { Secret } from "../../../models";
|
||||||
import {
|
import {
|
||||||
SecretSnapshot,
|
|
||||||
Log,
|
|
||||||
SecretVersion,
|
|
||||||
ISecretVersion,
|
|
||||||
FolderVersion,
|
FolderVersion,
|
||||||
|
ISecretVersion,
|
||||||
|
Log,
|
||||||
|
SecretSnapshot,
|
||||||
|
SecretVersion,
|
||||||
TFolderRootVersionSchema,
|
TFolderRootVersionSchema,
|
||||||
} from "../../models";
|
} from "../../models";
|
||||||
import { EESecretService } from "../../services";
|
import { EESecretService } from "../../services";
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import { Types } from 'mongoose';
|
import { Types } from "mongoose";
|
||||||
import { Action } from '../models';
|
import { Action } from "../models";
|
||||||
import {
|
import {
|
||||||
|
getLatestNSecretSecretVersionIds,
|
||||||
getLatestSecretVersionIds,
|
getLatestSecretVersionIds,
|
||||||
getLatestNSecretSecretVersionIds
|
} from "../helpers/secretVersion";
|
||||||
} from '../helpers/secretVersion';
|
|
||||||
import {
|
import {
|
||||||
|
ACTION_ADD_SECRETS,
|
||||||
|
ACTION_DELETE_SECRETS,
|
||||||
ACTION_LOGIN,
|
ACTION_LOGIN,
|
||||||
ACTION_LOGOUT,
|
ACTION_LOGOUT,
|
||||||
ACTION_ADD_SECRETS,
|
|
||||||
ACTION_READ_SECRETS,
|
ACTION_READ_SECRETS,
|
||||||
ACTION_DELETE_SECRETS,
|
|
||||||
ACTION_UPDATE_SECRETS,
|
ACTION_UPDATE_SECRETS,
|
||||||
} from '../../variables';
|
} from "../../variables";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an (audit) action for updating secrets
|
* Create an (audit) action for updating secrets
|
||||||
@@ -26,7 +26,7 @@ const createActionUpdateSecret = async ({
|
|||||||
serviceAccountId,
|
serviceAccountId,
|
||||||
serviceTokenDataId,
|
serviceTokenDataId,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
secretIds
|
secretIds,
|
||||||
}: {
|
}: {
|
||||||
name: string;
|
name: string;
|
||||||
userId?: Types.ObjectId;
|
userId?: Types.ObjectId;
|
||||||
@@ -37,11 +37,11 @@ const createActionUpdateSecret = async ({
|
|||||||
}) => {
|
}) => {
|
||||||
const latestSecretVersions = (await getLatestNSecretSecretVersionIds({
|
const latestSecretVersions = (await getLatestNSecretSecretVersionIds({
|
||||||
secretIds,
|
secretIds,
|
||||||
n: 2
|
n: 2,
|
||||||
}))
|
}))
|
||||||
.map((s) => ({
|
.map((s) => ({
|
||||||
oldSecretVersion: s.versions[0]._id,
|
oldSecretVersion: s.versions[0]._id,
|
||||||
newSecretVersion: s.versions[1]._id
|
newSecretVersion: s.versions[1]._id,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const action = await new Action({
|
const action = await new Action({
|
||||||
@@ -51,8 +51,8 @@ const createActionUpdateSecret = async ({
|
|||||||
serviceTokenData: serviceTokenDataId,
|
serviceTokenData: serviceTokenDataId,
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
payload: {
|
payload: {
|
||||||
secretVersions: latestSecretVersions
|
secretVersions: latestSecretVersions,
|
||||||
}
|
},
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
return action;
|
return action;
|
||||||
@@ -72,7 +72,7 @@ const createActionSecret = async ({
|
|||||||
serviceAccountId,
|
serviceAccountId,
|
||||||
serviceTokenDataId,
|
serviceTokenDataId,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
secretIds
|
secretIds,
|
||||||
}: {
|
}: {
|
||||||
name: string;
|
name: string;
|
||||||
userId?: Types.ObjectId;
|
userId?: Types.ObjectId;
|
||||||
@@ -84,10 +84,10 @@ const createActionSecret = async ({
|
|||||||
// case: action is adding, deleting, or reading secrets
|
// case: action is adding, deleting, or reading secrets
|
||||||
// -> add new secret versions
|
// -> add new secret versions
|
||||||
const latestSecretVersions = (await getLatestSecretVersionIds({
|
const latestSecretVersions = (await getLatestSecretVersionIds({
|
||||||
secretIds
|
secretIds,
|
||||||
}))
|
}))
|
||||||
.map((s) => ({
|
.map((s) => ({
|
||||||
newSecretVersion: s.versionId
|
newSecretVersion: s.versionId,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const action = await new Action({
|
const action = await new Action({
|
||||||
@@ -97,8 +97,8 @@ const createActionSecret = async ({
|
|||||||
serviceTokenData: serviceTokenDataId,
|
serviceTokenData: serviceTokenDataId,
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
payload: {
|
payload: {
|
||||||
secretVersions: latestSecretVersions
|
secretVersions: latestSecretVersions,
|
||||||
}
|
},
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
return action;
|
return action;
|
||||||
@@ -116,7 +116,7 @@ const createActionClient = ({
|
|||||||
name,
|
name,
|
||||||
userId,
|
userId,
|
||||||
serviceAccountId,
|
serviceAccountId,
|
||||||
serviceTokenDataId
|
serviceTokenDataId,
|
||||||
}: {
|
}: {
|
||||||
name: string;
|
name: string;
|
||||||
userId?: Types.ObjectId;
|
userId?: Types.ObjectId;
|
||||||
@@ -127,7 +127,7 @@ const createActionClient = ({
|
|||||||
name,
|
name,
|
||||||
user: userId,
|
user: userId,
|
||||||
serviceAccount: serviceAccountId,
|
serviceAccount: serviceAccountId,
|
||||||
serviceTokenData: serviceTokenDataId
|
serviceTokenData: serviceTokenDataId,
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
return action;
|
return action;
|
||||||
@@ -162,27 +162,27 @@ const createActionHelper = async ({
|
|||||||
case ACTION_LOGOUT:
|
case ACTION_LOGOUT:
|
||||||
action = await createActionClient({
|
action = await createActionClient({
|
||||||
name,
|
name,
|
||||||
userId
|
userId,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case ACTION_ADD_SECRETS:
|
case ACTION_ADD_SECRETS:
|
||||||
case ACTION_READ_SECRETS:
|
case ACTION_READ_SECRETS:
|
||||||
case ACTION_DELETE_SECRETS:
|
case ACTION_DELETE_SECRETS:
|
||||||
if (!workspaceId || !secretIds) throw new Error('Missing required params workspace id or secret ids to create action secret');
|
if (!workspaceId || !secretIds) throw new Error("Missing required params workspace id or secret ids to create action secret");
|
||||||
action = await createActionSecret({
|
action = await createActionSecret({
|
||||||
name,
|
name,
|
||||||
userId,
|
userId,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
secretIds
|
secretIds,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case ACTION_UPDATE_SECRETS:
|
case ACTION_UPDATE_SECRETS:
|
||||||
if (!workspaceId || !secretIds) throw new Error('Missing required params workspace id or secret ids to create action secret');
|
if (!workspaceId || !secretIds) throw new Error("Missing required params workspace id or secret ids to create action secret");
|
||||||
action = await createActionUpdateSecret({
|
action = await createActionUpdateSecret({
|
||||||
name,
|
name,
|
||||||
userId,
|
userId,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
secretIds
|
secretIds,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -191,5 +191,5 @@ const createActionHelper = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
createActionHelper
|
createActionHelper,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Types } from 'mongoose';
|
import { Types } from "mongoose";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { Membership } from "../../models";
|
import { Membership } from "../../models";
|
||||||
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from '../../variables';
|
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../../variables";
|
||||||
|
|
||||||
export const userHasWorkspaceAccess = async (userId: Types.ObjectId, workspaceId: Types.ObjectId, environment: string, action: any) => {
|
export const userHasWorkspaceAccess = async (userId: Types.ObjectId, workspaceId: Types.ObjectId, environment: string, action: any) => {
|
||||||
const membershipForWorkspace = await Membership.findOne({ workspace: workspaceId, user: userId })
|
const membershipForWorkspace = await Membership.findOne({ workspace: workspaceId, user: userId })
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Types } from 'mongoose';
|
import { Types } from "mongoose";
|
||||||
import {
|
import {
|
||||||
|
IAction,
|
||||||
Log,
|
Log,
|
||||||
IAction
|
} from "../models";
|
||||||
} from '../models';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an (audit) log
|
* Create an (audit) log
|
||||||
@@ -21,7 +21,7 @@ const createLogHelper = async ({
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
actions,
|
actions,
|
||||||
channel,
|
channel,
|
||||||
ipAddress
|
ipAddress,
|
||||||
}: {
|
}: {
|
||||||
userId?: Types.ObjectId;
|
userId?: Types.ObjectId;
|
||||||
serviceAccountId?: Types.ObjectId;
|
serviceAccountId?: Types.ObjectId;
|
||||||
@@ -39,12 +39,12 @@ const createLogHelper = async ({
|
|||||||
actionNames: actions.map((a) => a.name),
|
actionNames: actions.map((a) => a.name),
|
||||||
actions,
|
actions,
|
||||||
channel,
|
channel,
|
||||||
ipAddress
|
ipAddress,
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
return log;
|
return log;
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
createLogHelper
|
createLogHelper,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { Secret, ISecret } from "../../models";
|
import { Secret } from "../../models";
|
||||||
import {
|
import {
|
||||||
|
FolderVersion,
|
||||||
|
ISecretVersion,
|
||||||
SecretSnapshot,
|
SecretSnapshot,
|
||||||
SecretVersion,
|
SecretVersion,
|
||||||
ISecretVersion,
|
|
||||||
FolderVersion,
|
|
||||||
} from "../models";
|
} from "../models";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import requireLicenseAuth from './requireLicenseAuth';
|
import requireLicenseAuth from "./requireLicenseAuth";
|
||||||
import requireSecretSnapshotAuth from './requireSecretSnapshotAuth';
|
import requireSecretSnapshotAuth from "./requireSecretSnapshotAuth";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
requireLicenseAuth,
|
requireLicenseAuth,
|
||||||
requireSecretSnapshotAuth
|
requireSecretSnapshotAuth,
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Request, Response, NextFunction } from 'express';
|
import { NextFunction, Request, Response } from "express";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate if organization hosting meets license requirements to
|
* Validate if organization hosting meets license requirements to
|
||||||
@@ -7,7 +7,7 @@ import { Request, Response, NextFunction } from 'express';
|
|||||||
* @param {String[]} obj.acceptedTiers
|
* @param {String[]} obj.acceptedTiers
|
||||||
*/
|
*/
|
||||||
const requireLicenseAuth = ({
|
const requireLicenseAuth = ({
|
||||||
acceptedTiers
|
acceptedTiers,
|
||||||
}: {
|
}: {
|
||||||
acceptedTiers: string[];
|
acceptedTiers: string[];
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Request, Response, NextFunction } from 'express';
|
import { NextFunction, Request, Response } from "express";
|
||||||
import { UnauthorizedRequestError, SecretSnapshotNotFoundError } from '../../utils/errors';
|
import { SecretSnapshotNotFoundError } from "../../utils/errors";
|
||||||
import { SecretSnapshot } from '../models';
|
import { SecretSnapshot } from "../models";
|
||||||
import {
|
import {
|
||||||
validateMembership
|
validateMembership,
|
||||||
} from '../../helpers/membership';
|
} from "../../helpers/membership";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate if user on request has proper membership for secret snapshot
|
* Validate if user on request has proper membership for secret snapshot
|
||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
const requireSecretSnapshotAuth = ({
|
const requireSecretSnapshotAuth = ({
|
||||||
acceptedRoles,
|
acceptedRoles,
|
||||||
}: {
|
}: {
|
||||||
acceptedRoles: Array<'admin' | 'member'>;
|
acceptedRoles: Array<"admin" | "member">;
|
||||||
}) => {
|
}) => {
|
||||||
return async (req: Request, res: Response, next: NextFunction) => {
|
return async (req: Request, res: Response, next: NextFunction) => {
|
||||||
const { secretSnapshotId } = req.params;
|
const { secretSnapshotId } = req.params;
|
||||||
@@ -24,14 +24,14 @@ const requireSecretSnapshotAuth = ({
|
|||||||
|
|
||||||
if (!secretSnapshot) {
|
if (!secretSnapshot) {
|
||||||
return next(SecretSnapshotNotFoundError({
|
return next(SecretSnapshotNotFoundError({
|
||||||
message: 'Failed to find secret snapshot'
|
message: "Failed to find secret snapshot",
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
await validateMembership({
|
await validateMembership({
|
||||||
userId: req.user._id,
|
userId: req.user._id,
|
||||||
workspaceId: secretSnapshot.workspace,
|
workspaceId: secretSnapshot.workspace,
|
||||||
acceptedRoles
|
acceptedRoles,
|
||||||
});
|
});
|
||||||
|
|
||||||
req.secretSnapshot = secretSnapshot as any;
|
req.secretSnapshot = secretSnapshot as any;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { Schema, model, Types } from 'mongoose';
|
import { Schema, Types, model } from "mongoose";
|
||||||
import {
|
import {
|
||||||
|
ACTION_ADD_SECRETS,
|
||||||
|
ACTION_DELETE_SECRETS,
|
||||||
ACTION_LOGIN,
|
ACTION_LOGIN,
|
||||||
ACTION_LOGOUT,
|
ACTION_LOGOUT,
|
||||||
ACTION_ADD_SECRETS,
|
|
||||||
ACTION_UPDATE_SECRETS,
|
|
||||||
ACTION_READ_SECRETS,
|
ACTION_READ_SECRETS,
|
||||||
ACTION_DELETE_SECRETS
|
ACTION_UPDATE_SECRETS,
|
||||||
} from '../../variables';
|
} from "../../variables";
|
||||||
|
|
||||||
export interface IAction {
|
export interface IAction {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -30,42 +30,42 @@ const actionSchema = new Schema<IAction>(
|
|||||||
ACTION_ADD_SECRETS,
|
ACTION_ADD_SECRETS,
|
||||||
ACTION_UPDATE_SECRETS,
|
ACTION_UPDATE_SECRETS,
|
||||||
ACTION_READ_SECRETS,
|
ACTION_READ_SECRETS,
|
||||||
ACTION_DELETE_SECRETS
|
ACTION_DELETE_SECRETS,
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
user: {
|
user: {
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: 'User'
|
ref: "User",
|
||||||
},
|
},
|
||||||
serviceAccount: {
|
serviceAccount: {
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: 'ServiceAccount'
|
ref: "ServiceAccount",
|
||||||
},
|
},
|
||||||
serviceTokenData: {
|
serviceTokenData: {
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: 'ServiceTokenData'
|
ref: "ServiceTokenData",
|
||||||
},
|
},
|
||||||
workspace: {
|
workspace: {
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: 'Workspace'
|
ref: "Workspace",
|
||||||
},
|
},
|
||||||
payload: {
|
payload: {
|
||||||
secretVersions: [{
|
secretVersions: [{
|
||||||
oldSecretVersion: {
|
oldSecretVersion: {
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: 'SecretVersion'
|
ref: "SecretVersion",
|
||||||
},
|
},
|
||||||
newSecretVersion: {
|
newSecretVersion: {
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: 'SecretVersion'
|
ref: "SecretVersion",
|
||||||
}
|
},
|
||||||
}]
|
}],
|
||||||
}
|
},
|
||||||
}, {
|
}, {
|
||||||
timestamps: true
|
timestamps: true,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const Action = model<IAction>('Action', actionSchema);
|
const Action = model<IAction>("Action", actionSchema);
|
||||||
|
|
||||||
export default Action;
|
export default Action;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { model, Schema, Types } from "mongoose";
|
import { Schema, Types, model } from "mongoose";
|
||||||
|
|
||||||
export type TFolderRootVersionSchema = {
|
export type TFolderRootVersionSchema = {
|
||||||
_id: Types.ObjectId;
|
_id: Types.ObjectId;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { Schema, model, Types } from 'mongoose';
|
import { Schema, Types, model } from "mongoose";
|
||||||
import {
|
import {
|
||||||
|
ACTION_ADD_SECRETS,
|
||||||
|
ACTION_DELETE_SECRETS,
|
||||||
ACTION_LOGIN,
|
ACTION_LOGIN,
|
||||||
ACTION_LOGOUT,
|
ACTION_LOGOUT,
|
||||||
ACTION_ADD_SECRETS,
|
|
||||||
ACTION_UPDATE_SECRETS,
|
|
||||||
ACTION_READ_SECRETS,
|
ACTION_READ_SECRETS,
|
||||||
ACTION_DELETE_SECRETS
|
ACTION_UPDATE_SECRETS,
|
||||||
} from '../../variables';
|
} from "../../variables";
|
||||||
|
|
||||||
export interface ILog {
|
export interface ILog {
|
||||||
_id: Types.ObjectId;
|
_id: Types.ObjectId;
|
||||||
@@ -24,19 +24,19 @@ const logSchema = new Schema<ILog>(
|
|||||||
{
|
{
|
||||||
user: {
|
user: {
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: 'User'
|
ref: "User",
|
||||||
},
|
},
|
||||||
serviceAccount: {
|
serviceAccount: {
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: 'ServiceAccount'
|
ref: "ServiceAccount",
|
||||||
},
|
},
|
||||||
serviceTokenData: {
|
serviceTokenData: {
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: 'ServiceTokenData'
|
ref: "ServiceTokenData",
|
||||||
},
|
},
|
||||||
workspace: {
|
workspace: {
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: 'Workspace'
|
ref: "Workspace",
|
||||||
},
|
},
|
||||||
actionNames: {
|
actionNames: {
|
||||||
type: [String],
|
type: [String],
|
||||||
@@ -46,28 +46,28 @@ const logSchema = new Schema<ILog>(
|
|||||||
ACTION_ADD_SECRETS,
|
ACTION_ADD_SECRETS,
|
||||||
ACTION_UPDATE_SECRETS,
|
ACTION_UPDATE_SECRETS,
|
||||||
ACTION_READ_SECRETS,
|
ACTION_READ_SECRETS,
|
||||||
ACTION_DELETE_SECRETS
|
ACTION_DELETE_SECRETS,
|
||||||
],
|
],
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
actions: [{
|
actions: [{
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: 'Action',
|
ref: "Action",
|
||||||
required: true
|
required: true,
|
||||||
}],
|
}],
|
||||||
channel: {
|
channel: {
|
||||||
type: String,
|
type: String,
|
||||||
enum: ['web', 'cli', 'auto', 'k8-operator', 'other'],
|
enum: ["web", "cli", "auto", "k8-operator", "other"],
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
ipAddress: {
|
ipAddress: {
|
||||||
type: String
|
type: String,
|
||||||
}
|
},
|
||||||
}, {
|
}, {
|
||||||
timestamps: true
|
timestamps: true,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const Log = model<ILog>('Log', logSchema);
|
const Log = model<ILog>("Log", logSchema);
|
||||||
|
|
||||||
export default Log;
|
export default Log;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Schema, model, Types } from "mongoose";
|
import { Schema, Types, model } from "mongoose";
|
||||||
|
|
||||||
export interface ISecretSnapshot {
|
export interface ISecretSnapshot {
|
||||||
workspace: Types.ObjectId;
|
workspace: Types.ObjectId;
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Schema, model, Types } from "mongoose";
|
import { Schema, Types, model } from "mongoose";
|
||||||
import {
|
import {
|
||||||
SECRET_SHARED,
|
|
||||||
SECRET_PERSONAL,
|
|
||||||
ALGORITHM_AES_256_GCM,
|
ALGORITHM_AES_256_GCM,
|
||||||
ENCODING_SCHEME_UTF8,
|
|
||||||
ENCODING_SCHEME_BASE64,
|
ENCODING_SCHEME_BASE64,
|
||||||
|
ENCODING_SCHEME_UTF8,
|
||||||
|
SECRET_PERSONAL,
|
||||||
|
SECRET_SHARED,
|
||||||
} from "../../variables";
|
} from "../../variables";
|
||||||
|
|
||||||
export interface ISecretVersion {
|
export interface ISecretVersion {
|
||||||
@@ -114,9 +114,9 @@ const secretVersionSchema = new Schema<ISecretVersion>(
|
|||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
tags: {
|
tags: {
|
||||||
ref: 'Tag',
|
ref: "Tag",
|
||||||
type: [Schema.Types.ObjectId],
|
type: [Schema.Types.ObjectId],
|
||||||
default: []
|
default: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import express from 'express';
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import {
|
import {
|
||||||
validateRequest
|
validateRequest,
|
||||||
} from '../../../middleware';
|
} from "../../../middleware";
|
||||||
import { param } from 'express-validator';
|
import { param } from "express-validator";
|
||||||
import { actionController } from '../../controllers/v1';
|
import { actionController } from "../../controllers/v1";
|
||||||
|
|
||||||
// TODO: put into action controller
|
// TODO: put into action controller
|
||||||
router.get(
|
router.get(
|
||||||
'/:actionId',
|
"/:actionId",
|
||||||
param('actionId').exists().trim(),
|
param("actionId").exists().trim(),
|
||||||
validateRequest,
|
validateRequest,
|
||||||
actionController.getAction
|
actionController.getAction
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import express from 'express';
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import {
|
import {
|
||||||
requireAuth,
|
requireAuth,
|
||||||
validateRequest
|
validateRequest,
|
||||||
} from '../../../middleware';
|
} from "../../../middleware";
|
||||||
import { query } from 'express-validator';
|
import { query } from "express-validator";
|
||||||
import { cloudProductsController } from '../../controllers/v1';
|
import { cloudProductsController } from "../../controllers/v1";
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
'/',
|
"/",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: ['jwt', 'apiKey']
|
acceptedAuthModes: ["jwt", "apiKey"],
|
||||||
}),
|
}),
|
||||||
query('billing-cycle').exists().isIn(['monthly', 'yearly']),
|
query("billing-cycle").exists().isIn(["monthly", "yearly"]),
|
||||||
validateRequest,
|
validateRequest,
|
||||||
cloudProductsController.getCloudProducts
|
cloudProductsController.getCloudProducts
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import secret from './secret';
|
import secret from "./secret";
|
||||||
import secretSnapshot from './secretSnapshot';
|
import secretSnapshot from "./secretSnapshot";
|
||||||
import organizations from './organizations';
|
import organizations from "./organizations";
|
||||||
import workspace from './workspace';
|
import workspace from "./workspace";
|
||||||
import action from './action';
|
import action from "./action";
|
||||||
import cloudProducts from './cloudProducts';
|
import cloudProducts from "./cloudProducts";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
secret,
|
secret,
|
||||||
@@ -11,5 +11,5 @@ export {
|
|||||||
organizations,
|
organizations,
|
||||||
workspace,
|
workspace,
|
||||||
action,
|
action,
|
||||||
cloudProducts
|
cloudProducts,
|
||||||
}
|
}
|
||||||
@@ -1,86 +1,86 @@
|
|||||||
import express from 'express';
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import {
|
import {
|
||||||
requireAuth,
|
requireAuth,
|
||||||
requireOrganizationAuth,
|
requireOrganizationAuth,
|
||||||
validateRequest
|
validateRequest,
|
||||||
} from '../../../middleware';
|
} from "../../../middleware";
|
||||||
import { param, body, query } from 'express-validator';
|
import { body, param, query } from "express-validator";
|
||||||
import { organizationsController } from '../../controllers/v1';
|
import { organizationsController } from "../../controllers/v1";
|
||||||
import {
|
import {
|
||||||
OWNER, ADMIN, MEMBER, ACCEPTED
|
ACCEPTED, ADMIN, MEMBER, OWNER,
|
||||||
} from '../../../variables';
|
} from "../../../variables";
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
'/:organizationId/plan',
|
"/:organizationId/plan",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: ['jwt', 'apiKey']
|
acceptedAuthModes: ["jwt", "apiKey"],
|
||||||
}),
|
}),
|
||||||
requireOrganizationAuth({
|
requireOrganizationAuth({
|
||||||
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
||||||
acceptedStatuses: [ACCEPTED]
|
acceptedStatuses: [ACCEPTED],
|
||||||
}),
|
}),
|
||||||
param('organizationId').exists().trim(),
|
param("organizationId").exists().trim(),
|
||||||
query('workspaceId').optional().isString(),
|
query("workspaceId").optional().isString(),
|
||||||
validateRequest,
|
validateRequest,
|
||||||
organizationsController.getOrganizationPlan
|
organizationsController.getOrganizationPlan
|
||||||
);
|
);
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
'/:organizationId/plan',
|
"/:organizationId/plan",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: ['jwt', 'apiKey']
|
acceptedAuthModes: ["jwt", "apiKey"],
|
||||||
}),
|
}),
|
||||||
requireOrganizationAuth({
|
requireOrganizationAuth({
|
||||||
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
||||||
acceptedStatuses: [ACCEPTED]
|
acceptedStatuses: [ACCEPTED],
|
||||||
}),
|
}),
|
||||||
param('organizationId').exists().trim(),
|
param("organizationId").exists().trim(),
|
||||||
body('productId').exists().isString(),
|
body("productId").exists().isString(),
|
||||||
validateRequest,
|
validateRequest,
|
||||||
organizationsController.updateOrganizationPlan
|
organizationsController.updateOrganizationPlan
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
'/:organizationId/billing-details/payment-methods',
|
"/:organizationId/billing-details/payment-methods",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: ['jwt', 'apiKey']
|
acceptedAuthModes: ["jwt", "apiKey"],
|
||||||
}),
|
}),
|
||||||
requireOrganizationAuth({
|
requireOrganizationAuth({
|
||||||
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
||||||
acceptedStatuses: [ACCEPTED]
|
acceptedStatuses: [ACCEPTED],
|
||||||
}),
|
}),
|
||||||
param('organizationId').exists().trim(),
|
param("organizationId").exists().trim(),
|
||||||
validateRequest,
|
validateRequest,
|
||||||
organizationsController.getOrganizationPmtMethods
|
organizationsController.getOrganizationPmtMethods
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
'/:organizationId/billing-details/payment-methods',
|
"/:organizationId/billing-details/payment-methods",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: ['jwt', 'apiKey']
|
acceptedAuthModes: ["jwt", "apiKey"],
|
||||||
}),
|
}),
|
||||||
requireOrganizationAuth({
|
requireOrganizationAuth({
|
||||||
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
||||||
acceptedStatuses: [ACCEPTED]
|
acceptedStatuses: [ACCEPTED],
|
||||||
}),
|
}),
|
||||||
param('organizationId').exists().trim(),
|
param("organizationId").exists().trim(),
|
||||||
body('success_url').exists().isString(),
|
body("success_url").exists().isString(),
|
||||||
body('cancel_url').exists().isString(),
|
body("cancel_url").exists().isString(),
|
||||||
validateRequest,
|
validateRequest,
|
||||||
organizationsController.addOrganizationPmtMethod
|
organizationsController.addOrganizationPmtMethod
|
||||||
);
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
'/:organizationId/billing-details/payment-methods/:pmtMethodId',
|
"/:organizationId/billing-details/payment-methods/:pmtMethodId",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: ['jwt', 'apiKey']
|
acceptedAuthModes: ["jwt", "apiKey"],
|
||||||
}),
|
}),
|
||||||
requireOrganizationAuth({
|
requireOrganizationAuth({
|
||||||
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
||||||
acceptedStatuses: [ACCEPTED]
|
acceptedStatuses: [ACCEPTED],
|
||||||
}),
|
}),
|
||||||
param('organizationId').exists().trim(),
|
param("organizationId").exists().trim(),
|
||||||
validateRequest,
|
validateRequest,
|
||||||
organizationsController.deleteOrganizationPmtMethod
|
organizationsController.deleteOrganizationPmtMethod
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,46 +1,46 @@
|
|||||||
import express from 'express';
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import {
|
import {
|
||||||
requireAuth,
|
requireAuth,
|
||||||
requireSecretAuth,
|
requireSecretAuth,
|
||||||
validateRequest
|
validateRequest,
|
||||||
} from '../../../middleware';
|
} from "../../../middleware";
|
||||||
import { query, param, body } from 'express-validator';
|
import { body, param, query } from "express-validator";
|
||||||
import { secretController } from '../../controllers/v1';
|
import { secretController } from "../../controllers/v1";
|
||||||
import {
|
import {
|
||||||
ADMIN,
|
ADMIN,
|
||||||
MEMBER,
|
MEMBER,
|
||||||
PERMISSION_READ_SECRETS,
|
PERMISSION_READ_SECRETS,
|
||||||
PERMISSION_WRITE_SECRETS
|
PERMISSION_WRITE_SECRETS,
|
||||||
} from '../../../variables';
|
} from "../../../variables";
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
'/:secretId/secret-versions',
|
"/:secretId/secret-versions",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: ['jwt', 'apiKey']
|
acceptedAuthModes: ["jwt", "apiKey"],
|
||||||
}),
|
}),
|
||||||
requireSecretAuth({
|
requireSecretAuth({
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
requiredPermissions: [PERMISSION_READ_SECRETS]
|
requiredPermissions: [PERMISSION_READ_SECRETS],
|
||||||
}),
|
}),
|
||||||
param('secretId').exists().trim(),
|
param("secretId").exists().trim(),
|
||||||
query('offset').exists().isInt(),
|
query("offset").exists().isInt(),
|
||||||
query('limit').exists().isInt(),
|
query("limit").exists().isInt(),
|
||||||
validateRequest,
|
validateRequest,
|
||||||
secretController.getSecretVersions
|
secretController.getSecretVersions
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
'/:secretId/secret-versions/rollback',
|
"/:secretId/secret-versions/rollback",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: ['jwt', 'apiKey']
|
acceptedAuthModes: ["jwt", "apiKey"],
|
||||||
}),
|
}),
|
||||||
requireSecretAuth({
|
requireSecretAuth({
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
requiredPermissions: [PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS]
|
requiredPermissions: [PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS],
|
||||||
}),
|
}),
|
||||||
param('secretId').exists().trim(),
|
param("secretId").exists().trim(),
|
||||||
body('version').exists().isInt(),
|
body("version").exists().isInt(),
|
||||||
secretController.rollbackSecretVersion
|
secretController.rollbackSecretVersion
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
import express from 'express';
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import {
|
import {
|
||||||
requireSecretSnapshotAuth
|
requireSecretSnapshotAuth,
|
||||||
} from '../../middleware';
|
} from "../../middleware";
|
||||||
import {
|
import {
|
||||||
requireAuth,
|
requireAuth,
|
||||||
validateRequest
|
validateRequest,
|
||||||
} from '../../../middleware';
|
} from "../../../middleware";
|
||||||
import { param } from 'express-validator';
|
import { param } from "express-validator";
|
||||||
import { ADMIN, MEMBER } from '../../../variables';
|
import { ADMIN, MEMBER } from "../../../variables";
|
||||||
import { secretSnapshotController } from '../../controllers/v1';
|
import { secretSnapshotController } from "../../controllers/v1";
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
'/:secretSnapshotId',
|
"/:secretSnapshotId",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: ['jwt']
|
acceptedAuthModes: ["jwt"],
|
||||||
}),
|
}),
|
||||||
requireSecretSnapshotAuth({
|
requireSecretSnapshotAuth({
|
||||||
acceptedRoles: [ADMIN, MEMBER]
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
}),
|
}),
|
||||||
param('secretSnapshotId').exists().trim(),
|
param("secretSnapshotId").exists().trim(),
|
||||||
validateRequest,
|
validateRequest,
|
||||||
secretSnapshotController.getSecretSnapshot
|
secretSnapshotController.getSecretSnapshot
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import express from 'express';
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import { stripeController } from '../../controllers/v1';
|
import { stripeController } from "../../controllers/v1";
|
||||||
|
|
||||||
router.post('/webhook', stripeController.handleWebhook);
|
router.post("/webhook", stripeController.handleWebhook);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
requireWorkspaceAuth,
|
requireWorkspaceAuth,
|
||||||
validateRequest,
|
validateRequest,
|
||||||
} from "../../../middleware";
|
} from "../../../middleware";
|
||||||
import { param, query, body } from "express-validator";
|
import { body, param, query } from "express-validator";
|
||||||
import { ADMIN, MEMBER } from "../../../variables";
|
import { ADMIN, MEMBER } from "../../../variables";
|
||||||
import { workspaceController } from "../../controllers/v1";
|
import { workspaceController } from "../../controllers/v1";
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
import * as Sentry from '@sentry/node';
|
import * as Sentry from "@sentry/node";
|
||||||
import NodeCache from 'node-cache';
|
import NodeCache from "node-cache";
|
||||||
import {
|
import {
|
||||||
getLicenseKey,
|
getLicenseKey,
|
||||||
getLicenseServerKey,
|
getLicenseServerKey,
|
||||||
getLicenseServerUrl
|
getLicenseServerUrl,
|
||||||
} from '../../config';
|
} from "../../config";
|
||||||
import {
|
import {
|
||||||
licenseKeyRequest,
|
licenseKeyRequest,
|
||||||
licenseServerKeyRequest,
|
licenseServerKeyRequest,
|
||||||
|
refreshLicenseKeyToken,
|
||||||
refreshLicenseServerKeyToken,
|
refreshLicenseServerKeyToken,
|
||||||
refreshLicenseKeyToken
|
} from "../../config/request";
|
||||||
} from '../../config/request';
|
import { Organization } from "../../models";
|
||||||
import { Organization } from '../../models';
|
import { OrganizationNotFoundError } from "../../utils/errors";
|
||||||
import { OrganizationNotFoundError } from '../../utils/errors';
|
|
||||||
|
|
||||||
interface FeatureSet {
|
interface FeatureSet {
|
||||||
_id: string | null;
|
_id: string | null;
|
||||||
slug: 'starter' | 'team' | 'pro' | 'enterprise' | null;
|
slug: "starter" | "team" | "pro" | "enterprise" | null;
|
||||||
tier: number;
|
tier: number;
|
||||||
workspaceLimit: number | null;
|
workspaceLimit: number | null;
|
||||||
workspacesUsed: number;
|
workspacesUsed: number;
|
||||||
@@ -42,7 +42,7 @@ class EELicenseService {
|
|||||||
|
|
||||||
private readonly _isLicenseValid: boolean; // TODO: deprecate
|
private readonly _isLicenseValid: boolean; // TODO: deprecate
|
||||||
|
|
||||||
public instanceType: 'self-hosted' | 'enterprise-self-hosted' | 'cloud' = 'self-hosted';
|
public instanceType: "self-hosted" | "enterprise-self-hosted" | "cloud" = "self-hosted";
|
||||||
|
|
||||||
public globalFeatureSet: FeatureSet = {
|
public globalFeatureSet: FeatureSet = {
|
||||||
_id: null,
|
_id: null,
|
||||||
@@ -59,7 +59,7 @@ class EELicenseService {
|
|||||||
rbac: true,
|
rbac: true,
|
||||||
customRateLimits: true,
|
customRateLimits: true,
|
||||||
customAlerts: true,
|
customAlerts: true,
|
||||||
auditLogs: false
|
auditLogs: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
public localFeatureSet: NodeCache;
|
public localFeatureSet: NodeCache;
|
||||||
@@ -67,14 +67,14 @@ class EELicenseService {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this._isLicenseValid = true;
|
this._isLicenseValid = true;
|
||||||
this.localFeatureSet = new NodeCache({
|
this.localFeatureSet = new NodeCache({
|
||||||
stdTTL: 300
|
stdTTL: 300,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getPlan(organizationId: string, workspaceId?: string): Promise<FeatureSet> {
|
public async getPlan(organizationId: string, workspaceId?: string): Promise<FeatureSet> {
|
||||||
try {
|
try {
|
||||||
if (this.instanceType === 'cloud') {
|
if (this.instanceType === "cloud") {
|
||||||
const cachedPlan = this.localFeatureSet.get<FeatureSet>(`${organizationId}-${workspaceId ?? ''}`);
|
const cachedPlan = this.localFeatureSet.get<FeatureSet>(`${organizationId}-${workspaceId ?? ""}`);
|
||||||
if (cachedPlan) {
|
if (cachedPlan) {
|
||||||
return cachedPlan;
|
return cachedPlan;
|
||||||
}
|
}
|
||||||
@@ -91,7 +91,7 @@ class EELicenseService {
|
|||||||
const { data: { currentPlan } } = await licenseServerKeyRequest.get(url);
|
const { data: { currentPlan } } = await licenseServerKeyRequest.get(url);
|
||||||
|
|
||||||
// cache fetched plan for organization
|
// cache fetched plan for organization
|
||||||
this.localFeatureSet.set(`${organizationId}-${workspaceId ?? ''}`, currentPlan);
|
this.localFeatureSet.set(`${organizationId}-${workspaceId ?? ""}`, currentPlan);
|
||||||
|
|
||||||
return currentPlan;
|
return currentPlan;
|
||||||
}
|
}
|
||||||
@@ -103,8 +103,8 @@ class EELicenseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async refreshPlan(organizationId: string, workspaceId?: string) {
|
public async refreshPlan(organizationId: string, workspaceId?: string) {
|
||||||
if (this.instanceType === 'cloud') {
|
if (this.instanceType === "cloud") {
|
||||||
this.localFeatureSet.del(`${organizationId}-${workspaceId ?? ''}`);
|
this.localFeatureSet.del(`${organizationId}-${workspaceId ?? ""}`);
|
||||||
await this.getPlan(organizationId, workspaceId);
|
await this.getPlan(organizationId, workspaceId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,7 +119,7 @@ class EELicenseService {
|
|||||||
const token = await refreshLicenseServerKeyToken()
|
const token = await refreshLicenseServerKeyToken()
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
this.instanceType = 'cloud';
|
this.instanceType = "cloud";
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -135,7 +135,7 @@ class EELicenseService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.globalFeatureSet = currentPlan;
|
this.globalFeatureSet = currentPlan;
|
||||||
this.instanceType = 'enterprise-self-hosted';
|
this.instanceType = "enterprise-self-hosted";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { Types } from 'mongoose';
|
import { Types } from "mongoose";
|
||||||
import {
|
import {
|
||||||
IAction
|
IAction,
|
||||||
} from '../models';
|
} from "../models";
|
||||||
import {
|
import {
|
||||||
createLogHelper
|
createLogHelper,
|
||||||
} from '../helpers/log';
|
} from "../helpers/log";
|
||||||
import {
|
import {
|
||||||
createActionHelper
|
createActionHelper,
|
||||||
} from '../helpers/action';
|
} from "../helpers/action";
|
||||||
import EELicenseService from './EELicenseService';
|
import EELicenseService from "./EELicenseService";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to handle Enterprise Edition log actions
|
* Class to handle Enterprise Edition log actions
|
||||||
@@ -31,7 +31,7 @@ class EELogService {
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
actions,
|
actions,
|
||||||
channel,
|
channel,
|
||||||
ipAddress
|
ipAddress,
|
||||||
}: {
|
}: {
|
||||||
userId?: Types.ObjectId;
|
userId?: Types.ObjectId;
|
||||||
serviceAccountId?: Types.ObjectId;
|
serviceAccountId?: Types.ObjectId;
|
||||||
@@ -49,7 +49,7 @@ class EELogService {
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
actions,
|
actions,
|
||||||
channel,
|
channel,
|
||||||
ipAddress
|
ipAddress,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ class EELogService {
|
|||||||
serviceAccountId,
|
serviceAccountId,
|
||||||
serviceTokenDataId,
|
serviceTokenDataId,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
secretIds
|
secretIds,
|
||||||
}: {
|
}: {
|
||||||
name: string;
|
name: string;
|
||||||
userId?: Types.ObjectId;
|
userId?: Types.ObjectId;
|
||||||
@@ -83,7 +83,7 @@ class EELogService {
|
|||||||
serviceAccountId,
|
serviceAccountId,
|
||||||
serviceTokenDataId,
|
serviceTokenDataId,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
secretIds
|
secretIds,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Types } from 'mongoose';
|
import { Types } from "mongoose";
|
||||||
import { ISecretVersion } from '../models';
|
import { ISecretVersion } from "../models";
|
||||||
import {
|
import {
|
||||||
takeSecretSnapshotHelper,
|
|
||||||
addSecretVersionsHelper,
|
addSecretVersionsHelper,
|
||||||
markDeletedSecretVersionsHelper,
|
markDeletedSecretVersionsHelper,
|
||||||
} from '../helpers/secret';
|
takeSecretSnapshotHelper,
|
||||||
import EELicenseService from './EELicenseService';
|
} from "../helpers/secret";
|
||||||
|
import EELicenseService from "./EELicenseService";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to handle Enterprise Edition secret actions
|
* Class to handle Enterprise Edition secret actions
|
||||||
|
|||||||
@@ -5,5 +5,5 @@ import EELogService from "./EELogService";
|
|||||||
export {
|
export {
|
||||||
EELicenseService,
|
EELicenseService,
|
||||||
EESecretService,
|
EESecretService,
|
||||||
EELogService
|
EELogService,
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { eventPushSecrets } from "./secret"
|
import { eventPushSecrets } from "./secret"
|
||||||
|
|
||||||
export {
|
export {
|
||||||
eventPushSecrets
|
eventPushSecrets,
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Types } from 'mongoose';
|
import { Types } from "mongoose";
|
||||||
import {
|
import {
|
||||||
|
EVENT_PULL_SECRETS,
|
||||||
EVENT_PUSH_SECRETS,
|
EVENT_PUSH_SECRETS,
|
||||||
EVENT_PULL_SECRETS
|
} from "../variables";
|
||||||
} from '../variables';
|
|
||||||
|
|
||||||
interface PushSecret {
|
interface PushSecret {
|
||||||
ciphertextKey: string;
|
ciphertextKey: string;
|
||||||
@@ -13,7 +13,7 @@ interface PushSecret {
|
|||||||
ivValue: string;
|
ivValue: string;
|
||||||
tagValue: string;
|
tagValue: string;
|
||||||
hashValue: string;
|
hashValue: string;
|
||||||
type: 'shared' | 'personal';
|
type: "shared" | "personal";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,7 +24,7 @@ interface PushSecret {
|
|||||||
*/
|
*/
|
||||||
const eventPushSecrets = ({
|
const eventPushSecrets = ({
|
||||||
workspaceId,
|
workspaceId,
|
||||||
environment
|
environment,
|
||||||
}: {
|
}: {
|
||||||
workspaceId: Types.ObjectId;
|
workspaceId: Types.ObjectId;
|
||||||
environment?: string;
|
environment?: string;
|
||||||
@@ -35,7 +35,7 @@ const eventPushSecrets = ({
|
|||||||
environment,
|
environment,
|
||||||
payload: {
|
payload: {
|
||||||
|
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,10 +55,10 @@ const eventPullSecrets = ({
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
payload: {
|
payload: {
|
||||||
|
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
eventPushSecrets
|
eventPushSecrets,
|
||||||
}
|
}
|
||||||
|
|||||||
+89
-89
@@ -1,36 +1,36 @@
|
|||||||
import { Types } from 'mongoose';
|
import { Types } from "mongoose";
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from "jsonwebtoken";
|
||||||
import bcrypt from 'bcrypt';
|
import bcrypt from "bcrypt";
|
||||||
import {
|
import {
|
||||||
IUser,
|
|
||||||
User,
|
|
||||||
ServiceTokenData,
|
|
||||||
ServiceAccount,
|
|
||||||
APIKeyData,
|
APIKeyData,
|
||||||
|
ITokenVersion,
|
||||||
|
IUser,
|
||||||
|
ServiceAccount,
|
||||||
|
ServiceTokenData,
|
||||||
TokenVersion,
|
TokenVersion,
|
||||||
ITokenVersion
|
User,
|
||||||
} from '../models';
|
} from "../models";
|
||||||
import {
|
import {
|
||||||
AccountNotFoundError,
|
|
||||||
ServiceTokenDataNotFoundError,
|
|
||||||
ServiceAccountNotFoundError,
|
|
||||||
APIKeyDataNotFoundError,
|
APIKeyDataNotFoundError,
|
||||||
|
AccountNotFoundError,
|
||||||
|
BadRequestError,
|
||||||
|
ServiceAccountNotFoundError,
|
||||||
|
ServiceTokenDataNotFoundError,
|
||||||
UnauthorizedRequestError,
|
UnauthorizedRequestError,
|
||||||
BadRequestError
|
} from "../utils/errors";
|
||||||
} from '../utils/errors';
|
|
||||||
import {
|
import {
|
||||||
getJwtAuthLifetime,
|
getJwtAuthLifetime,
|
||||||
getJwtAuthSecret,
|
getJwtAuthSecret,
|
||||||
getJwtProviderAuthSecret,
|
getJwtProviderAuthSecret,
|
||||||
getJwtRefreshLifetime,
|
getJwtRefreshLifetime,
|
||||||
getJwtRefreshSecret
|
getJwtRefreshSecret,
|
||||||
} from '../config';
|
} from "../config";
|
||||||
import {
|
import {
|
||||||
|
AUTH_MODE_API_KEY,
|
||||||
AUTH_MODE_JWT,
|
AUTH_MODE_JWT,
|
||||||
AUTH_MODE_SERVICE_ACCOUNT,
|
AUTH_MODE_SERVICE_ACCOUNT,
|
||||||
AUTH_MODE_SERVICE_TOKEN,
|
AUTH_MODE_SERVICE_TOKEN,
|
||||||
AUTH_MODE_API_KEY
|
} from "../variables";
|
||||||
} from '../variables';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -39,41 +39,41 @@ import {
|
|||||||
*/
|
*/
|
||||||
export const validateAuthMode = ({
|
export const validateAuthMode = ({
|
||||||
headers,
|
headers,
|
||||||
acceptedAuthModes
|
acceptedAuthModes,
|
||||||
}: {
|
}: {
|
||||||
headers: { [key: string]: string | string[] | undefined },
|
headers: { [key: string]: string | string[] | undefined },
|
||||||
acceptedAuthModes: string[]
|
acceptedAuthModes: string[]
|
||||||
}) => {
|
}) => {
|
||||||
const apiKey = headers['x-api-key'];
|
const apiKey = headers["x-api-key"];
|
||||||
const authHeader = headers['authorization'];
|
const authHeader = headers["authorization"];
|
||||||
|
|
||||||
let authMode, authTokenValue;
|
let authMode, authTokenValue;
|
||||||
if (apiKey === undefined && authHeader === undefined) {
|
if (apiKey === undefined && authHeader === undefined) {
|
||||||
// case: no auth or X-API-KEY header present
|
// case: no auth or X-API-KEY header present
|
||||||
throw BadRequestError({ message: 'Missing Authorization or X-API-KEY in request header.' });
|
throw BadRequestError({ message: "Missing Authorization or X-API-KEY in request header." });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof apiKey === 'string') {
|
if (typeof apiKey === "string") {
|
||||||
// case: treat request authentication type as via X-API-KEY (i.e. API Key)
|
// case: treat request authentication type as via X-API-KEY (i.e. API Key)
|
||||||
authMode = AUTH_MODE_API_KEY;
|
authMode = AUTH_MODE_API_KEY;
|
||||||
authTokenValue = apiKey;
|
authTokenValue = apiKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof authHeader === 'string') {
|
if (typeof authHeader === "string") {
|
||||||
// case: treat request authentication type as via Authorization header (i.e. either JWT or service token)
|
// case: treat request authentication type as via Authorization header (i.e. either JWT or service token)
|
||||||
const [tokenType, tokenValue] = <[string, string]>authHeader.split(' ', 2) ?? [null, null]
|
const [tokenType, tokenValue] = <[string, string]>authHeader.split(" ", 2) ?? [null, null]
|
||||||
if (tokenType === null)
|
if (tokenType === null)
|
||||||
throw BadRequestError({ message: `Missing Authorization Header in the request header.` });
|
throw BadRequestError({ message: "Missing Authorization Header in the request header." });
|
||||||
if (tokenType.toLowerCase() !== 'bearer')
|
if (tokenType.toLowerCase() !== "bearer")
|
||||||
throw BadRequestError({ message: `The provided authentication type '${tokenType}' is not supported.` });
|
throw BadRequestError({ message: `The provided authentication type '${tokenType}' is not supported.` });
|
||||||
if (tokenValue === null)
|
if (tokenValue === null)
|
||||||
throw BadRequestError({ message: 'Missing Authorization Body in the request header.' });
|
throw BadRequestError({ message: "Missing Authorization Body in the request header." });
|
||||||
|
|
||||||
switch (tokenValue.split('.', 1)[0]) {
|
switch (tokenValue.split(".", 1)[0]) {
|
||||||
case 'st':
|
case "st":
|
||||||
authMode = AUTH_MODE_SERVICE_TOKEN;
|
authMode = AUTH_MODE_SERVICE_TOKEN;
|
||||||
break;
|
break;
|
||||||
case 'sa':
|
case "sa":
|
||||||
authMode = AUTH_MODE_SERVICE_ACCOUNT;
|
authMode = AUTH_MODE_SERVICE_ACCOUNT;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -83,13 +83,13 @@ export const validateAuthMode = ({
|
|||||||
authTokenValue = tokenValue;
|
authTokenValue = tokenValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!authMode || !authTokenValue) throw BadRequestError({ message: 'Missing valid Authorization or X-API-KEY in request header.' });
|
if (!authMode || !authTokenValue) throw BadRequestError({ message: "Missing valid Authorization or X-API-KEY in request header." });
|
||||||
|
|
||||||
if (!acceptedAuthModes.includes(authMode)) throw BadRequestError({ message: 'The provided authentication type is not supported.' });
|
if (!acceptedAuthModes.includes(authMode)) throw BadRequestError({ message: "The provided authentication type is not supported." });
|
||||||
|
|
||||||
return ({
|
return ({
|
||||||
authMode,
|
authMode,
|
||||||
authTokenValue
|
authTokenValue,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ export const validateAuthMode = ({
|
|||||||
* @returns {User} user - user corresponding to JWT token
|
* @returns {User} user - user corresponding to JWT token
|
||||||
*/
|
*/
|
||||||
export const getAuthUserPayload = async ({
|
export const getAuthUserPayload = async ({
|
||||||
authTokenValue
|
authTokenValue,
|
||||||
}: {
|
}: {
|
||||||
authTokenValue: string;
|
authTokenValue: string;
|
||||||
}) => {
|
}) => {
|
||||||
@@ -109,31 +109,31 @@ export const getAuthUserPayload = async ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
_id: new Types.ObjectId(decodedToken.userId)
|
_id: new Types.ObjectId(decodedToken.userId),
|
||||||
}).select('+publicKey +accessVersion');
|
}).select("+publicKey +accessVersion");
|
||||||
|
|
||||||
if (!user) throw AccountNotFoundError({ message: 'Failed to find user' });
|
if (!user) throw AccountNotFoundError({ message: "Failed to find user" });
|
||||||
|
|
||||||
if (!user?.publicKey) throw UnauthorizedRequestError({ message: 'Failed to authenticate user with partially set up account' });
|
if (!user?.publicKey) throw UnauthorizedRequestError({ message: "Failed to authenticate user with partially set up account" });
|
||||||
|
|
||||||
const tokenVersion = await TokenVersion.findOneAndUpdate({
|
const tokenVersion = await TokenVersion.findOneAndUpdate({
|
||||||
_id: new Types.ObjectId(decodedToken.tokenVersionId),
|
_id: new Types.ObjectId(decodedToken.tokenVersionId),
|
||||||
user: user._id
|
user: user._id,
|
||||||
}, {
|
}, {
|
||||||
lastUsed: new Date()
|
lastUsed: new Date(),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!tokenVersion) throw UnauthorizedRequestError({
|
if (!tokenVersion) throw UnauthorizedRequestError({
|
||||||
message: 'Failed to validate access token'
|
message: "Failed to validate access token",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (decodedToken.accessVersion !== tokenVersion.accessVersion) throw UnauthorizedRequestError({
|
if (decodedToken.accessVersion !== tokenVersion.accessVersion) throw UnauthorizedRequestError({
|
||||||
message: 'Failed to validate access token'
|
message: "Failed to validate access token",
|
||||||
});
|
});
|
||||||
|
|
||||||
return ({
|
return ({
|
||||||
user,
|
user,
|
||||||
tokenVersionId: tokenVersion._id
|
tokenVersionId: tokenVersion._id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,41 +144,41 @@ export const getAuthUserPayload = async ({
|
|||||||
* @returns {ServiceTokenData} serviceTokenData - service token data
|
* @returns {ServiceTokenData} serviceTokenData - service token data
|
||||||
*/
|
*/
|
||||||
export const getAuthSTDPayload = async ({
|
export const getAuthSTDPayload = async ({
|
||||||
authTokenValue
|
authTokenValue,
|
||||||
}: {
|
}: {
|
||||||
authTokenValue: string;
|
authTokenValue: string;
|
||||||
}) => {
|
}) => {
|
||||||
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split('.', 3);
|
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split(".", 3);
|
||||||
|
|
||||||
let serviceTokenData = await ServiceTokenData
|
let serviceTokenData = await ServiceTokenData
|
||||||
.findById(TOKEN_IDENTIFIER, '+secretHash +expiresAt');
|
.findById(TOKEN_IDENTIFIER, "+secretHash +expiresAt");
|
||||||
|
|
||||||
if (!serviceTokenData) {
|
if (!serviceTokenData) {
|
||||||
throw ServiceTokenDataNotFoundError({ message: 'Failed to find service token data' });
|
throw ServiceTokenDataNotFoundError({ message: "Failed to find service token data" });
|
||||||
} else if (serviceTokenData?.expiresAt && new Date(serviceTokenData.expiresAt) < new Date()) {
|
} else if (serviceTokenData?.expiresAt && new Date(serviceTokenData.expiresAt) < new Date()) {
|
||||||
// case: service token expired
|
// case: service token expired
|
||||||
await ServiceTokenData.findByIdAndDelete(serviceTokenData._id);
|
await ServiceTokenData.findByIdAndDelete(serviceTokenData._id);
|
||||||
throw UnauthorizedRequestError({
|
throw UnauthorizedRequestError({
|
||||||
message: 'Failed to authenticate expired service token'
|
message: "Failed to authenticate expired service token",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const isMatch = await bcrypt.compare(TOKEN_SECRET, serviceTokenData.secretHash);
|
const isMatch = await bcrypt.compare(TOKEN_SECRET, serviceTokenData.secretHash);
|
||||||
if (!isMatch) throw UnauthorizedRequestError({
|
if (!isMatch) throw UnauthorizedRequestError({
|
||||||
message: 'Failed to authenticate service token'
|
message: "Failed to authenticate service token",
|
||||||
});
|
});
|
||||||
|
|
||||||
serviceTokenData = await ServiceTokenData
|
serviceTokenData = await ServiceTokenData
|
||||||
.findOneAndUpdate({
|
.findOneAndUpdate({
|
||||||
_id: new Types.ObjectId(TOKEN_IDENTIFIER)
|
_id: new Types.ObjectId(TOKEN_IDENTIFIER),
|
||||||
}, {
|
}, {
|
||||||
lastUsed: new Date()
|
lastUsed: new Date(),
|
||||||
}, {
|
}, {
|
||||||
new: true
|
new: true,
|
||||||
})
|
})
|
||||||
.select('+encryptedKey +iv +tag');
|
.select("+encryptedKey +iv +tag");
|
||||||
|
|
||||||
if (!serviceTokenData) throw ServiceTokenDataNotFoundError({ message: 'Failed to find service token data' });
|
if (!serviceTokenData) throw ServiceTokenDataNotFoundError({ message: "Failed to find service token data" });
|
||||||
|
|
||||||
return serviceTokenData;
|
return serviceTokenData;
|
||||||
}
|
}
|
||||||
@@ -190,23 +190,23 @@ export const getAuthSTDPayload = async ({
|
|||||||
* @returns {ServiceAccount} serviceAccount
|
* @returns {ServiceAccount} serviceAccount
|
||||||
*/
|
*/
|
||||||
export const getAuthSAAKPayload = async ({
|
export const getAuthSAAKPayload = async ({
|
||||||
authTokenValue
|
authTokenValue,
|
||||||
}: {
|
}: {
|
||||||
authTokenValue: string;
|
authTokenValue: string;
|
||||||
}) => {
|
}) => {
|
||||||
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split('.', 3);
|
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split(".", 3);
|
||||||
|
|
||||||
const serviceAccount = await ServiceAccount.findById(
|
const serviceAccount = await ServiceAccount.findById(
|
||||||
Buffer.from(TOKEN_IDENTIFIER, 'base64').toString('hex')
|
Buffer.from(TOKEN_IDENTIFIER, "base64").toString("hex")
|
||||||
).select('+secretHash');
|
).select("+secretHash");
|
||||||
|
|
||||||
if (!serviceAccount) {
|
if (!serviceAccount) {
|
||||||
throw ServiceAccountNotFoundError({ message: 'Failed to find service account' });
|
throw ServiceAccountNotFoundError({ message: "Failed to find service account" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await bcrypt.compare(TOKEN_SECRET, serviceAccount.secretHash);
|
const result = await bcrypt.compare(TOKEN_SECRET, serviceAccount.secretHash);
|
||||||
if (!result) throw UnauthorizedRequestError({
|
if (!result) throw UnauthorizedRequestError({
|
||||||
message: 'Failed to authenticate service account access key'
|
message: "Failed to authenticate service account access key",
|
||||||
});
|
});
|
||||||
|
|
||||||
return serviceAccount;
|
return serviceAccount;
|
||||||
@@ -219,48 +219,48 @@ export const getAuthSAAKPayload = async ({
|
|||||||
* @returns {APIKeyData} apiKeyData - API key data
|
* @returns {APIKeyData} apiKeyData - API key data
|
||||||
*/
|
*/
|
||||||
export const getAuthAPIKeyPayload = async ({
|
export const getAuthAPIKeyPayload = async ({
|
||||||
authTokenValue
|
authTokenValue,
|
||||||
}: {
|
}: {
|
||||||
authTokenValue: string;
|
authTokenValue: string;
|
||||||
}) => {
|
}) => {
|
||||||
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split('.', 3);
|
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split(".", 3);
|
||||||
|
|
||||||
let apiKeyData = await APIKeyData
|
let apiKeyData = await APIKeyData
|
||||||
.findById(TOKEN_IDENTIFIER, '+secretHash +expiresAt')
|
.findById(TOKEN_IDENTIFIER, "+secretHash +expiresAt")
|
||||||
.populate<{ user: IUser }>('user', '+publicKey');
|
.populate<{ user: IUser }>("user", "+publicKey");
|
||||||
|
|
||||||
if (!apiKeyData) {
|
if (!apiKeyData) {
|
||||||
throw APIKeyDataNotFoundError({ message: 'Failed to find API key data' });
|
throw APIKeyDataNotFoundError({ message: "Failed to find API key data" });
|
||||||
} else if (apiKeyData?.expiresAt && new Date(apiKeyData.expiresAt) < new Date()) {
|
} else if (apiKeyData?.expiresAt && new Date(apiKeyData.expiresAt) < new Date()) {
|
||||||
// case: API key expired
|
// case: API key expired
|
||||||
await APIKeyData.findByIdAndDelete(apiKeyData._id);
|
await APIKeyData.findByIdAndDelete(apiKeyData._id);
|
||||||
throw UnauthorizedRequestError({
|
throw UnauthorizedRequestError({
|
||||||
message: 'Failed to authenticate expired API key'
|
message: "Failed to authenticate expired API key",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const isMatch = await bcrypt.compare(TOKEN_SECRET, apiKeyData.secretHash);
|
const isMatch = await bcrypt.compare(TOKEN_SECRET, apiKeyData.secretHash);
|
||||||
if (!isMatch) throw UnauthorizedRequestError({
|
if (!isMatch) throw UnauthorizedRequestError({
|
||||||
message: 'Failed to authenticate API key'
|
message: "Failed to authenticate API key",
|
||||||
});
|
});
|
||||||
|
|
||||||
apiKeyData = await APIKeyData.findOneAndUpdate({
|
apiKeyData = await APIKeyData.findOneAndUpdate({
|
||||||
_id: new Types.ObjectId(TOKEN_IDENTIFIER)
|
_id: new Types.ObjectId(TOKEN_IDENTIFIER),
|
||||||
}, {
|
}, {
|
||||||
lastUsed: new Date()
|
lastUsed: new Date(),
|
||||||
}, {
|
}, {
|
||||||
new: true
|
new: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!apiKeyData) {
|
if (!apiKeyData) {
|
||||||
throw APIKeyDataNotFoundError({ message: 'Failed to find API key data' });
|
throw APIKeyDataNotFoundError({ message: "Failed to find API key data" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await User.findById(apiKeyData.user).select('+publicKey');
|
const user = await User.findById(apiKeyData.user).select("+publicKey");
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw AccountNotFoundError({
|
throw AccountNotFoundError({
|
||||||
message: 'Failed to find user'
|
message: "Failed to find user",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,7 +278,7 @@ export const getAuthAPIKeyPayload = async ({
|
|||||||
export const issueAuthTokens = async ({
|
export const issueAuthTokens = async ({
|
||||||
userId,
|
userId,
|
||||||
ip,
|
ip,
|
||||||
userAgent
|
userAgent,
|
||||||
}: {
|
}: {
|
||||||
userId: Types.ObjectId;
|
userId: Types.ObjectId;
|
||||||
ip: string;
|
ip: string;
|
||||||
@@ -290,7 +290,7 @@ export const issueAuthTokens = async ({
|
|||||||
tokenVersion = await TokenVersion.findOne({
|
tokenVersion = await TokenVersion.findOne({
|
||||||
user: userId,
|
user: userId,
|
||||||
ip,
|
ip,
|
||||||
userAgent
|
userAgent,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!tokenVersion) {
|
if (!tokenVersion) {
|
||||||
@@ -302,7 +302,7 @@ export const issueAuthTokens = async ({
|
|||||||
accessVersion: 0,
|
accessVersion: 0,
|
||||||
ip,
|
ip,
|
||||||
userAgent,
|
userAgent,
|
||||||
lastUsed: new Date()
|
lastUsed: new Date(),
|
||||||
}).save();
|
}).save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,25 +311,25 @@ export const issueAuthTokens = async ({
|
|||||||
payload: {
|
payload: {
|
||||||
userId,
|
userId,
|
||||||
tokenVersionId: tokenVersion._id.toString(),
|
tokenVersionId: tokenVersion._id.toString(),
|
||||||
accessVersion: tokenVersion.accessVersion
|
accessVersion: tokenVersion.accessVersion,
|
||||||
},
|
},
|
||||||
expiresIn: await getJwtAuthLifetime(),
|
expiresIn: await getJwtAuthLifetime(),
|
||||||
secret: await getJwtAuthSecret()
|
secret: await getJwtAuthSecret(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const refreshToken = createToken({
|
const refreshToken = createToken({
|
||||||
payload: {
|
payload: {
|
||||||
userId,
|
userId,
|
||||||
tokenVersionId: tokenVersion._id.toString(),
|
tokenVersionId: tokenVersion._id.toString(),
|
||||||
refreshVersion: tokenVersion.refreshVersion
|
refreshVersion: tokenVersion.refreshVersion,
|
||||||
},
|
},
|
||||||
expiresIn: await getJwtRefreshLifetime(),
|
expiresIn: await getJwtRefreshLifetime(),
|
||||||
secret: await getJwtRefreshSecret()
|
secret: await getJwtRefreshSecret(),
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
token,
|
token,
|
||||||
refreshToken
|
refreshToken,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -342,12 +342,12 @@ export const clearTokens = async (tokenVersionId: Types.ObjectId): Promise<void>
|
|||||||
// increment refreshVersion on user by 1
|
// increment refreshVersion on user by 1
|
||||||
|
|
||||||
await TokenVersion.findOneAndUpdate({
|
await TokenVersion.findOneAndUpdate({
|
||||||
_id: tokenVersionId
|
_id: tokenVersionId,
|
||||||
}, {
|
}, {
|
||||||
$inc: {
|
$inc: {
|
||||||
refreshVersion: 1,
|
refreshVersion: 1,
|
||||||
accessVersion: 1
|
accessVersion: 1,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -362,14 +362,14 @@ export const clearTokens = async (tokenVersionId: Types.ObjectId): Promise<void>
|
|||||||
export const createToken = ({
|
export const createToken = ({
|
||||||
payload,
|
payload,
|
||||||
expiresIn,
|
expiresIn,
|
||||||
secret
|
secret,
|
||||||
}: {
|
}: {
|
||||||
payload: any;
|
payload: any;
|
||||||
expiresIn: string | number;
|
expiresIn: string | number;
|
||||||
secret: string;
|
secret: string;
|
||||||
}) => {
|
}) => {
|
||||||
return jwt.sign(payload, secret, {
|
return jwt.sign(payload, secret, {
|
||||||
expiresIn
|
expiresIn,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -383,7 +383,7 @@ export const validateProviderAuthToken = async ({
|
|||||||
providerAuthToken?: string;
|
providerAuthToken?: string;
|
||||||
}) => {
|
}) => {
|
||||||
if (!providerAuthToken) {
|
if (!providerAuthToken) {
|
||||||
throw new Error('Invalid authentication request.');
|
throw new Error("Invalid authentication request.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const decodedToken = <jwt.ProviderAuthJwtPayload>(
|
const decodedToken = <jwt.ProviderAuthJwtPayload>(
|
||||||
@@ -394,6 +394,6 @@ export const validateProviderAuthToken = async ({
|
|||||||
decodedToken.authProvider !== user.authProvider ||
|
decodedToken.authProvider !== user.authProvider ||
|
||||||
decodedToken.email !== email
|
decodedToken.email !== email
|
||||||
) {
|
) {
|
||||||
throw new Error('Invalid authentication credentials.')
|
throw new Error("Invalid authentication credentials.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { Bot, BotKey, Secret, ISecret, IUser } from "../models";
|
import { Bot, BotKey, ISecret, IUser, Secret } from "../models";
|
||||||
import {
|
import {
|
||||||
generateKeyPair,
|
|
||||||
encryptSymmetric128BitHexKeyUTF8,
|
|
||||||
decryptSymmetric128BitHexKeyUTF8,
|
|
||||||
decryptAsymmetric,
|
decryptAsymmetric,
|
||||||
|
decryptSymmetric128BitHexKeyUTF8,
|
||||||
|
encryptSymmetric128BitHexKeyUTF8,
|
||||||
|
generateKeyPair,
|
||||||
} from "../utils/crypto";
|
} from "../utils/crypto";
|
||||||
import {
|
import {
|
||||||
SECRET_SHARED,
|
|
||||||
ALGORITHM_AES_256_GCM,
|
ALGORITHM_AES_256_GCM,
|
||||||
ENCODING_SCHEME_UTF8,
|
|
||||||
ENCODING_SCHEME_BASE64,
|
ENCODING_SCHEME_BASE64,
|
||||||
|
ENCODING_SCHEME_UTF8,
|
||||||
|
SECRET_SHARED,
|
||||||
} from "../variables";
|
} from "../variables";
|
||||||
import { getEncryptionKey, getRootEncryptionKey, client } from "../config";
|
import { client, getEncryptionKey, getRootEncryptionKey } from "../config";
|
||||||
import { InternalServerError } from "../utils/errors";
|
import { InternalServerError } from "../utils/errors";
|
||||||
import Folder from "../models/folder";
|
import Folder from "../models/folder";
|
||||||
import { getFolderByPath } from "../services/FolderService";
|
import { getFolderByPath } from "../services/FolderService";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import mongoose from 'mongoose';
|
import mongoose from "mongoose";
|
||||||
import { getLogger } from '../utils/logger';
|
import { getLogger } from "../utils/logger";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize database connection
|
* Initialize database connection
|
||||||
@@ -8,7 +8,7 @@ import { getLogger } from '../utils/logger';
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const initDatabaseHelper = async ({
|
export const initDatabaseHelper = async ({
|
||||||
mongoURL
|
mongoURL,
|
||||||
}: {
|
}: {
|
||||||
mongoURL: string;
|
mongoURL: string;
|
||||||
}) => {
|
}) => {
|
||||||
@@ -16,7 +16,7 @@ export const initDatabaseHelper = async ({
|
|||||||
await mongoose.connect(mongoURL);
|
await mongoose.connect(mongoURL);
|
||||||
|
|
||||||
// allow empty strings to pass the required validator
|
// allow empty strings to pass the required validator
|
||||||
mongoose.Schema.Types.String.checkRequired(v => typeof v === 'string');
|
mongoose.Schema.Types.String.checkRequired(v => typeof v === "string");
|
||||||
|
|
||||||
(await getLogger("database")).info("Database connection established");
|
(await getLogger("database")).info("Database connection established");
|
||||||
|
|
||||||
@@ -35,10 +35,10 @@ export const closeDatabaseHelper = async () => {
|
|||||||
new Promise((resolve) => {
|
new Promise((resolve) => {
|
||||||
if (mongoose.connection && mongoose.connection.readyState == 1) {
|
if (mongoose.connection && mongoose.connection.readyState == 1) {
|
||||||
mongoose.connection.close()
|
mongoose.connection.close()
|
||||||
.then(() => resolve('Database connection closed'));
|
.then(() => resolve("Database connection closed"));
|
||||||
} else {
|
} else {
|
||||||
resolve('Database connection already closed');
|
resolve("Database connection already closed");
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { Bot, IBot } from "../models";
|
import { Bot } from "../models";
|
||||||
import { EVENT_PUSH_SECRETS } from "../variables";
|
import { EVENT_PUSH_SECRETS } from "../variables";
|
||||||
import { IntegrationService } from "../services";
|
import { IntegrationService } from "../services";
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
export * from './auth';
|
export * from "./auth";
|
||||||
export * from './bot';
|
export * from "./bot";
|
||||||
export * from './database';
|
export * from "./database";
|
||||||
export * from './event';
|
export * from "./event";
|
||||||
export * from './integration';
|
export * from "./integration";
|
||||||
export * from './key';
|
export * from "./key";
|
||||||
export * from './membership';
|
export * from "./membership";
|
||||||
export * from './membershipOrg';
|
export * from "./membershipOrg";
|
||||||
export * from './nodemailer';
|
export * from "./nodemailer";
|
||||||
export * from './organization';
|
export * from "./organization";
|
||||||
export * from './rateLimiter';
|
export * from "./rateLimiter";
|
||||||
export * from './secret';
|
export * from "./secret";
|
||||||
export * from './secrets';
|
export * from "./secrets";
|
||||||
export * from './signup';
|
export * from "./signup";
|
||||||
export * from './token';
|
export * from "./token";
|
||||||
export * from './user';
|
export * from "./user";
|
||||||
export * from './workspace';
|
export * from "./workspace";
|
||||||
@@ -3,10 +3,10 @@ import { Bot, Integration, IntegrationAuth } from "../models";
|
|||||||
import { exchangeCode, exchangeRefresh, syncSecrets } from "../integrations";
|
import { exchangeCode, exchangeRefresh, syncSecrets } from "../integrations";
|
||||||
import { BotService } from "../services";
|
import { BotService } from "../services";
|
||||||
import {
|
import {
|
||||||
INTEGRATION_VERCEL,
|
|
||||||
INTEGRATION_NETLIFY,
|
|
||||||
ALGORITHM_AES_256_GCM,
|
ALGORITHM_AES_256_GCM,
|
||||||
ENCODING_SCHEME_UTF8,
|
ENCODING_SCHEME_UTF8,
|
||||||
|
INTEGRATION_NETLIFY,
|
||||||
|
INTEGRATION_VERCEL,
|
||||||
} from "../variables";
|
} from "../variables";
|
||||||
import { UnauthorizedRequestError } from "../utils/errors";
|
import { UnauthorizedRequestError } from "../utils/errors";
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Key, IKey } from '../models';
|
import { IKey, Key } from "../models";
|
||||||
|
|
||||||
interface Key {
|
interface Key {
|
||||||
encryptedKey: string;
|
encryptedKey: string;
|
||||||
@@ -20,7 +20,7 @@ interface Key {
|
|||||||
export const pushKeys = async ({
|
export const pushKeys = async ({
|
||||||
userId,
|
userId,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
keys
|
keys,
|
||||||
}: {
|
}: {
|
||||||
userId: string;
|
userId: string;
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
@@ -31,9 +31,9 @@ export const pushKeys = async ({
|
|||||||
(
|
(
|
||||||
await Key.find(
|
await Key.find(
|
||||||
{
|
{
|
||||||
workspace: workspaceId
|
workspace: workspaceId,
|
||||||
},
|
},
|
||||||
'receiver'
|
"receiver"
|
||||||
)
|
)
|
||||||
).map((k: IKey) => k.receiver.toString())
|
).map((k: IKey) => k.receiver.toString())
|
||||||
);
|
);
|
||||||
@@ -47,7 +47,7 @@ export const pushKeys = async ({
|
|||||||
nonce: k.nonce,
|
nonce: k.nonce,
|
||||||
sender: userId,
|
sender: userId,
|
||||||
receiver: k.userId,
|
receiver: k.userId,
|
||||||
workspace: workspaceId
|
workspace: workspaceId,
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { Membership, Key } from "../models";
|
import { Key, Membership } from "../models";
|
||||||
import { MembershipNotFoundError, BadRequestError } from "../utils/errors";
|
import { BadRequestError, MembershipNotFoundError } from "../utils/errors";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate that user with id [userId] is a member of workspace with id [workspaceId]
|
* Validate that user with id [userId] is a member of workspace with id [workspaceId]
|
||||||
@@ -62,7 +62,7 @@ export const findMembership = async (queryObj: any) => {
|
|||||||
export const addMemberships = async ({
|
export const addMemberships = async ({
|
||||||
userIds,
|
userIds,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
roles
|
roles,
|
||||||
}: {
|
}: {
|
||||||
userIds: string[];
|
userIds: string[];
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
@@ -95,7 +95,7 @@ export const addMemberships = async ({
|
|||||||
*/
|
*/
|
||||||
export const deleteMembership = async ({ membershipId }: { membershipId: string }) => {
|
export const deleteMembership = async ({ membershipId }: { membershipId: string }) => {
|
||||||
const deletedMembership = await Membership.findOneAndDelete({
|
const deletedMembership = await Membership.findOneAndDelete({
|
||||||
_id: membershipId
|
_id: membershipId,
|
||||||
});
|
});
|
||||||
|
|
||||||
// delete keys associated with the membership
|
// delete keys associated with the membership
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { Types } from 'mongoose';
|
import { Types } from "mongoose";
|
||||||
import {
|
import {
|
||||||
MembershipOrg,
|
Key,
|
||||||
Workspace,
|
|
||||||
Membership,
|
Membership,
|
||||||
Key
|
MembershipOrg,
|
||||||
} from '../models';
|
Workspace,
|
||||||
|
} from "../models";
|
||||||
import {
|
import {
|
||||||
MembershipOrgNotFoundError,
|
MembershipOrgNotFoundError,
|
||||||
UnauthorizedRequestError
|
UnauthorizedRequestError,
|
||||||
} from '../utils/errors';
|
} from "../utils/errors";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate that user with id [userId] is a member of organization with id [organizationId]
|
* Validate that user with id [userId] is a member of organization with id [organizationId]
|
||||||
@@ -22,31 +22,31 @@ export const validateMembershipOrg = async ({
|
|||||||
userId,
|
userId,
|
||||||
organizationId,
|
organizationId,
|
||||||
acceptedRoles,
|
acceptedRoles,
|
||||||
acceptedStatuses
|
acceptedStatuses,
|
||||||
}: {
|
}: {
|
||||||
userId: Types.ObjectId;
|
userId: Types.ObjectId;
|
||||||
organizationId: Types.ObjectId;
|
organizationId: Types.ObjectId;
|
||||||
acceptedRoles?: Array<'owner' | 'admin' | 'member'>;
|
acceptedRoles?: Array<"owner" | "admin" | "member">;
|
||||||
acceptedStatuses?: Array<'invited' | 'accepted'>;
|
acceptedStatuses?: Array<"invited" | "accepted">;
|
||||||
}) => {
|
}) => {
|
||||||
const membershipOrg = await MembershipOrg.findOne({
|
const membershipOrg = await MembershipOrg.findOne({
|
||||||
user: userId,
|
user: userId,
|
||||||
organization: organizationId
|
organization: organizationId,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!membershipOrg) {
|
if (!membershipOrg) {
|
||||||
throw MembershipOrgNotFoundError({ message: 'Failed to find organization membership' });
|
throw MembershipOrgNotFoundError({ message: "Failed to find organization membership" });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (acceptedRoles) {
|
if (acceptedRoles) {
|
||||||
if (!acceptedRoles.includes(membershipOrg.role)) {
|
if (!acceptedRoles.includes(membershipOrg.role)) {
|
||||||
throw UnauthorizedRequestError({ message: 'Failed to validate organization membership role' });
|
throw UnauthorizedRequestError({ message: "Failed to validate organization membership role" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (acceptedStatuses) {
|
if (acceptedStatuses) {
|
||||||
if (!acceptedStatuses.includes(membershipOrg.status)) {
|
if (!acceptedStatuses.includes(membershipOrg.status)) {
|
||||||
throw UnauthorizedRequestError({ message: 'Failed to validate organization membership status' });
|
throw UnauthorizedRequestError({ message: "Failed to validate organization membership status" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ export const addMembershipsOrg = async ({
|
|||||||
userIds,
|
userIds,
|
||||||
organizationId,
|
organizationId,
|
||||||
roles,
|
roles,
|
||||||
statuses
|
statuses,
|
||||||
}: {
|
}: {
|
||||||
userIds: string[];
|
userIds: string[];
|
||||||
organizationId: string;
|
organizationId: string;
|
||||||
@@ -90,16 +90,16 @@ export const addMembershipsOrg = async ({
|
|||||||
user: userId,
|
user: userId,
|
||||||
organization: organizationId,
|
organization: organizationId,
|
||||||
role: roles[idx],
|
role: roles[idx],
|
||||||
status: statuses[idx]
|
status: statuses[idx],
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
user: userId,
|
user: userId,
|
||||||
organization: organizationId,
|
organization: organizationId,
|
||||||
role: roles[idx],
|
role: roles[idx],
|
||||||
status: statuses[idx]
|
status: statuses[idx],
|
||||||
},
|
},
|
||||||
upsert: true
|
upsert: true,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -112,15 +112,15 @@ export const addMembershipsOrg = async ({
|
|||||||
* @param {String} obj.membershipOrgId - id of organization membership to delete
|
* @param {String} obj.membershipOrgId - id of organization membership to delete
|
||||||
*/
|
*/
|
||||||
export const deleteMembershipOrg = async ({
|
export const deleteMembershipOrg = async ({
|
||||||
membershipOrgId
|
membershipOrgId,
|
||||||
}: {
|
}: {
|
||||||
membershipOrgId: string;
|
membershipOrgId: string;
|
||||||
}) => {
|
}) => {
|
||||||
const deletedMembershipOrg = await MembershipOrg.findOneAndDelete({
|
const deletedMembershipOrg = await MembershipOrg.findOneAndDelete({
|
||||||
_id: membershipOrgId
|
_id: membershipOrgId,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!deletedMembershipOrg) throw new Error('Failed to delete organization membership');
|
if (!deletedMembershipOrg) throw new Error("Failed to delete organization membership");
|
||||||
|
|
||||||
// delete keys associated with organization membership
|
// delete keys associated with organization membership
|
||||||
if (deletedMembershipOrg?.user) {
|
if (deletedMembershipOrg?.user) {
|
||||||
@@ -128,22 +128,22 @@ export const deleteMembershipOrg = async ({
|
|||||||
|
|
||||||
const workspaces = (
|
const workspaces = (
|
||||||
await Workspace.find({
|
await Workspace.find({
|
||||||
organization: deletedMembershipOrg.organization
|
organization: deletedMembershipOrg.organization,
|
||||||
})
|
})
|
||||||
).map((w) => w._id.toString());
|
).map((w) => w._id.toString());
|
||||||
|
|
||||||
await Membership.deleteMany({
|
await Membership.deleteMany({
|
||||||
user: deletedMembershipOrg.user,
|
user: deletedMembershipOrg.user,
|
||||||
workspace: {
|
workspace: {
|
||||||
$in: workspaces
|
$in: workspaces,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
await Key.deleteMany({
|
await Key.deleteMany({
|
||||||
receiver: deletedMembershipOrg.user,
|
receiver: deletedMembershipOrg.user,
|
||||||
workspace: {
|
workspace: {
|
||||||
$in: workspaces
|
$in: workspaces,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import fs from 'fs';
|
import fs from "fs";
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
import handlebars from 'handlebars';
|
import handlebars from "handlebars";
|
||||||
import nodemailer from 'nodemailer';
|
import nodemailer from "nodemailer";
|
||||||
import { getSmtpFromName, getSmtpFromAddress, getSmtpConfigured } from '../config';
|
import { getSmtpConfigured, getSmtpFromAddress, getSmtpFromName } from "../config";
|
||||||
|
|
||||||
let smtpTransporter: nodemailer.Transporter;
|
let smtpTransporter: nodemailer.Transporter;
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ export const sendMail = async ({
|
|||||||
template,
|
template,
|
||||||
subjectLine,
|
subjectLine,
|
||||||
recipients,
|
recipients,
|
||||||
substitutions
|
substitutions,
|
||||||
}: {
|
}: {
|
||||||
template: string;
|
template: string;
|
||||||
subjectLine: string;
|
subjectLine: string;
|
||||||
@@ -26,17 +26,17 @@ export const sendMail = async ({
|
|||||||
}) => {
|
}) => {
|
||||||
if (await getSmtpConfigured()) {
|
if (await getSmtpConfigured()) {
|
||||||
const html = fs.readFileSync(
|
const html = fs.readFileSync(
|
||||||
path.resolve(__dirname, '../templates/' + template),
|
path.resolve(__dirname, "../templates/" + template),
|
||||||
'utf8'
|
"utf8"
|
||||||
);
|
);
|
||||||
const temp = handlebars.compile(html);
|
const temp = handlebars.compile(html);
|
||||||
const htmlToSend = temp(substitutions);
|
const htmlToSend = temp(substitutions);
|
||||||
|
|
||||||
await smtpTransporter.sendMail({
|
await smtpTransporter.sendMail({
|
||||||
from: `"${await getSmtpFromName()}" <${await getSmtpFromAddress()}>`,
|
from: `"${await getSmtpFromName()}" <${await getSmtpFromAddress()}>`,
|
||||||
to: recipients.join(', '),
|
to: recipients.join(", "),
|
||||||
subject: subjectLine,
|
subject: subjectLine,
|
||||||
html: htmlToSend
|
html: htmlToSend,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
import Stripe from "stripe";
|
import Stripe from "stripe";
|
||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { Organization, MembershipOrg } from "../models";
|
import { MembershipOrg, Organization } from "../models";
|
||||||
import {
|
import {
|
||||||
ACCEPTED
|
ACCEPTED,
|
||||||
} from "../variables";
|
} from "../variables";
|
||||||
import {
|
import {
|
||||||
getStripeSecretKey,
|
|
||||||
getStripeProductPro,
|
getStripeProductPro,
|
||||||
getStripeProductTeam,
|
|
||||||
getStripeProductStarter,
|
getStripeProductStarter,
|
||||||
|
getStripeProductTeam,
|
||||||
|
getStripeSecretKey,
|
||||||
} from "../config";
|
} from "../config";
|
||||||
import {
|
import {
|
||||||
EELicenseService
|
EELicenseService,
|
||||||
} from '../ee/services';
|
} from "../ee/services";
|
||||||
import {
|
import {
|
||||||
getLicenseServerUrl
|
getLicenseServerUrl,
|
||||||
} from '../config';
|
} from "../config";
|
||||||
import {
|
import {
|
||||||
|
licenseKeyRequest,
|
||||||
licenseServerKeyRequest,
|
licenseServerKeyRequest,
|
||||||
licenseKeyRequest
|
} from "../config/request";
|
||||||
} from '../config/request';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an organization with name [name]
|
* Create an organization with name [name]
|
||||||
@@ -137,7 +137,7 @@ export const updateSubscriptionOrgQuantity = async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (organization && organization.customerId) {
|
if (organization && organization.customerId) {
|
||||||
if (EELicenseService.instanceType === 'cloud') {
|
if (EELicenseService.instanceType === "cloud") {
|
||||||
// instance of Infisical is a cloud instance
|
// instance of Infisical is a cloud instance
|
||||||
const quantity = await MembershipOrg.countDocuments({
|
const quantity = await MembershipOrg.countDocuments({
|
||||||
organization: new Types.ObjectId(organizationId),
|
organization: new Types.ObjectId(organizationId),
|
||||||
@@ -147,7 +147,7 @@ export const updateSubscriptionOrgQuantity = async ({
|
|||||||
await licenseServerKeyRequest.patch(
|
await licenseServerKeyRequest.patch(
|
||||||
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${organization.customerId}/cloud-plan`,
|
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${organization.customerId}/cloud-plan`,
|
||||||
{
|
{
|
||||||
quantity
|
quantity,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -155,17 +155,17 @@ export const updateSubscriptionOrgQuantity = async ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (EELicenseService.instanceType === 'enterprise-self-hosted') {
|
if (EELicenseService.instanceType === "enterprise-self-hosted") {
|
||||||
// instance of Infisical is an enterprise self-hosted instance
|
// instance of Infisical is an enterprise self-hosted instance
|
||||||
|
|
||||||
const usedSeats = await MembershipOrg.countDocuments({
|
const usedSeats = await MembershipOrg.countDocuments({
|
||||||
status: ACCEPTED
|
status: ACCEPTED,
|
||||||
});
|
});
|
||||||
|
|
||||||
await licenseKeyRequest.patch(
|
await licenseKeyRequest.patch(
|
||||||
`${await getLicenseServerUrl()}/api/license/v1/license`,
|
`${await getLicenseServerUrl()}/api/license/v1/license`,
|
||||||
{
|
{
|
||||||
usedSeats
|
usedSeats,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import rateLimit from 'express-rate-limit';
|
import rateLimit from "express-rate-limit";
|
||||||
// const MongoStore = require('rate-limit-mongo');
|
// const MongoStore = require('rate-limit-mongo');
|
||||||
|
|
||||||
// 200 per minute
|
// 200 per minute
|
||||||
@@ -14,11 +14,11 @@ export const apiLimiter = rateLimit({
|
|||||||
standardHeaders: true,
|
standardHeaders: true,
|
||||||
legacyHeaders: false,
|
legacyHeaders: false,
|
||||||
skip: (request) => {
|
skip: (request) => {
|
||||||
return request.path === '/healthcheck' || request.path === '/api/status'
|
return request.path === "/healthcheck" || request.path === "/api/status"
|
||||||
},
|
},
|
||||||
keyGenerator: (req, res) => {
|
keyGenerator: (req, res) => {
|
||||||
return req.realIP
|
return req.realIP
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 50 requests per 1 hours
|
// 50 requests per 1 hours
|
||||||
@@ -35,7 +35,7 @@ const authLimit = rateLimit({
|
|||||||
legacyHeaders: false,
|
legacyHeaders: false,
|
||||||
keyGenerator: (req, res) => {
|
keyGenerator: (req, res) => {
|
||||||
return req.realIP
|
return req.realIP
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 5 requests per 1 hour
|
// 5 requests per 1 hour
|
||||||
@@ -52,11 +52,11 @@ export const passwordLimiter = rateLimit({
|
|||||||
legacyHeaders: false,
|
legacyHeaders: false,
|
||||||
keyGenerator: (req, res) => {
|
keyGenerator: (req, res) => {
|
||||||
return req.realIP
|
return req.realIP
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const authLimiter = (req: any, res: any, next: any) => {
|
export const authLimiter = (req: any, res: any, next: any) => {
|
||||||
if (process.env.NODE_ENV === 'production') {
|
if (process.env.NODE_ENV === "production") {
|
||||||
authLimit(req, res, next);
|
authLimit(req, res, next);
|
||||||
} else {
|
} else {
|
||||||
next();
|
next();
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { Secret, ISecret } from "../models";
|
import { ISecret, Secret } from "../models";
|
||||||
import { EESecretService, EELogService } from "../ee/services";
|
import { EELogService, EESecretService } from "../ee/services";
|
||||||
import { IAction, SecretVersion } from "../ee/models";
|
import { IAction, SecretVersion } from "../ee/models";
|
||||||
import {
|
import {
|
||||||
SECRET_SHARED,
|
|
||||||
SECRET_PERSONAL,
|
|
||||||
ACTION_ADD_SECRETS,
|
ACTION_ADD_SECRETS,
|
||||||
ACTION_UPDATE_SECRETS,
|
|
||||||
ACTION_DELETE_SECRETS,
|
ACTION_DELETE_SECRETS,
|
||||||
ACTION_READ_SECRETS,
|
ACTION_READ_SECRETS,
|
||||||
|
ACTION_UPDATE_SECRETS,
|
||||||
ALGORITHM_AES_256_GCM,
|
ALGORITHM_AES_256_GCM,
|
||||||
ENCODING_SCHEME_UTF8,
|
ENCODING_SCHEME_UTF8,
|
||||||
|
SECRET_PERSONAL,
|
||||||
|
SECRET_SHARED,
|
||||||
} from "../variables";
|
} from "../variables";
|
||||||
|
|
||||||
interface V1PushSecret {
|
interface V1PushSecret {
|
||||||
|
|||||||
@@ -1,45 +1,45 @@
|
|||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import {
|
import {
|
||||||
CreateSecretParams,
|
CreateSecretParams,
|
||||||
GetSecretsParams,
|
|
||||||
GetSecretParams,
|
|
||||||
UpdateSecretParams,
|
|
||||||
DeleteSecretParams,
|
DeleteSecretParams,
|
||||||
} from '../interfaces/services/SecretService';
|
GetSecretParams,
|
||||||
|
GetSecretsParams,
|
||||||
|
UpdateSecretParams,
|
||||||
|
} from "../interfaces/services/SecretService";
|
||||||
import {
|
import {
|
||||||
Secret,
|
|
||||||
ISecret,
|
ISecret,
|
||||||
|
Secret,
|
||||||
SecretBlindIndexData,
|
SecretBlindIndexData,
|
||||||
ServiceTokenData,
|
ServiceTokenData,
|
||||||
} from "../models";
|
} from "../models";
|
||||||
import { SecretVersion } from "../ee/models";
|
import { SecretVersion } from "../ee/models";
|
||||||
import {
|
import {
|
||||||
BadRequestError,
|
BadRequestError,
|
||||||
SecretNotFoundError,
|
|
||||||
SecretBlindIndexDataNotFoundError,
|
|
||||||
InternalServerError,
|
InternalServerError,
|
||||||
|
SecretBlindIndexDataNotFoundError,
|
||||||
|
SecretNotFoundError,
|
||||||
UnauthorizedRequestError,
|
UnauthorizedRequestError,
|
||||||
} from "../utils/errors";
|
} from "../utils/errors";
|
||||||
import {
|
import {
|
||||||
SECRET_PERSONAL,
|
|
||||||
SECRET_SHARED,
|
|
||||||
ACTION_ADD_SECRETS,
|
ACTION_ADD_SECRETS,
|
||||||
|
ACTION_DELETE_SECRETS,
|
||||||
ACTION_READ_SECRETS,
|
ACTION_READ_SECRETS,
|
||||||
ACTION_UPDATE_SECRETS,
|
ACTION_UPDATE_SECRETS,
|
||||||
ACTION_DELETE_SECRETS,
|
|
||||||
ALGORITHM_AES_256_GCM,
|
ALGORITHM_AES_256_GCM,
|
||||||
ENCODING_SCHEME_UTF8,
|
|
||||||
ENCODING_SCHEME_BASE64,
|
ENCODING_SCHEME_BASE64,
|
||||||
|
ENCODING_SCHEME_UTF8,
|
||||||
|
SECRET_PERSONAL,
|
||||||
|
SECRET_SHARED,
|
||||||
} from "../variables";
|
} from "../variables";
|
||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
import * as argon2 from "argon2";
|
import * as argon2 from "argon2";
|
||||||
import {
|
import {
|
||||||
encryptSymmetric128BitHexKeyUTF8,
|
|
||||||
decryptSymmetric128BitHexKeyUTF8,
|
decryptSymmetric128BitHexKeyUTF8,
|
||||||
} from '../utils/crypto';
|
encryptSymmetric128BitHexKeyUTF8,
|
||||||
import { TelemetryService } from '../services';
|
} from "../utils/crypto";
|
||||||
import { getEncryptionKey, client, getRootEncryptionKey } from "../config";
|
import { TelemetryService } from "../services";
|
||||||
import { EESecretService, EELogService } from "../ee/services";
|
import { client, getEncryptionKey, getRootEncryptionKey } from "../config";
|
||||||
|
import { EELogService, EESecretService } from "../ee/services";
|
||||||
import {
|
import {
|
||||||
getAuthDataPayloadIdObj,
|
getAuthDataPayloadIdObj,
|
||||||
getAuthDataPayloadUserObj,
|
getAuthDataPayloadUserObj,
|
||||||
@@ -56,7 +56,7 @@ import { getFolderIdFromServiceToken } from "../services/FolderService";
|
|||||||
*/
|
*/
|
||||||
export const repackageSecretToRaw = ({
|
export const repackageSecretToRaw = ({
|
||||||
secret,
|
secret,
|
||||||
key
|
key,
|
||||||
}: {
|
}: {
|
||||||
secret: ISecret;
|
secret: ISecret;
|
||||||
key: string;
|
key: string;
|
||||||
@@ -66,24 +66,24 @@ export const repackageSecretToRaw = ({
|
|||||||
ciphertext: secret.secretKeyCiphertext,
|
ciphertext: secret.secretKeyCiphertext,
|
||||||
iv: secret.secretKeyIV,
|
iv: secret.secretKeyIV,
|
||||||
tag: secret.secretKeyTag,
|
tag: secret.secretKeyTag,
|
||||||
key
|
key,
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretValue = decryptSymmetric128BitHexKeyUTF8({
|
const secretValue = decryptSymmetric128BitHexKeyUTF8({
|
||||||
ciphertext: secret.secretValueCiphertext,
|
ciphertext: secret.secretValueCiphertext,
|
||||||
iv: secret.secretValueIV,
|
iv: secret.secretValueIV,
|
||||||
tag: secret.secretValueTag,
|
tag: secret.secretValueTag,
|
||||||
key
|
key,
|
||||||
});
|
});
|
||||||
|
|
||||||
let secretComment: string = '';
|
let secretComment = "";
|
||||||
|
|
||||||
if (secret.secretCommentCiphertext && secret.secretCommentIV && secret.secretCommentTag) {
|
if (secret.secretCommentCiphertext && secret.secretCommentIV && secret.secretCommentTag) {
|
||||||
secretComment = decryptSymmetric128BitHexKeyUTF8({
|
secretComment = decryptSymmetric128BitHexKeyUTF8({
|
||||||
ciphertext: secret.secretCommentCiphertext,
|
ciphertext: secret.secretCommentCiphertext,
|
||||||
iv: secret.secretCommentIV,
|
iv: secret.secretCommentIV,
|
||||||
tag: secret.secretCommentTag,
|
tag: secret.secretCommentTag,
|
||||||
key
|
key,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ export const repackageSecretToRaw = ({
|
|||||||
user: secret.user,
|
user: secret.user,
|
||||||
secretKey,
|
secretKey,
|
||||||
secretValue,
|
secretValue,
|
||||||
secretComment
|
secretComment,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -944,6 +944,6 @@ export const deleteSecretHelper = async ({
|
|||||||
|
|
||||||
return ({
|
return ({
|
||||||
secrets,
|
secrets,
|
||||||
secret
|
secret,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { IUser } from '../models';
|
import { IUser } from "../models";
|
||||||
import { createOrganization } from './organization';
|
import { createOrganization } from "./organization";
|
||||||
import { addMembershipsOrg } from './membershipOrg';
|
import { addMembershipsOrg } from "./membershipOrg";
|
||||||
import { OWNER, ACCEPTED } from '../variables';
|
import { ACCEPTED, OWNER } from "../variables";
|
||||||
import { sendMail } from '../helpers/nodemailer';
|
import { sendMail } from "../helpers/nodemailer";
|
||||||
import { TokenService } from '../services';
|
import { TokenService } from "../services";
|
||||||
import { TOKEN_EMAIL_CONFIRMATION } from '../variables';
|
import { TOKEN_EMAIL_CONFIRMATION } from "../variables";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send magic link to verify email to [email]
|
* Send magic link to verify email to [email]
|
||||||
@@ -16,17 +16,17 @@ import { TOKEN_EMAIL_CONFIRMATION } from '../variables';
|
|||||||
export const sendEmailVerification = async ({ email }: { email: string }) => {
|
export const sendEmailVerification = async ({ email }: { email: string }) => {
|
||||||
const token = await TokenService.createToken({
|
const token = await TokenService.createToken({
|
||||||
type: TOKEN_EMAIL_CONFIRMATION,
|
type: TOKEN_EMAIL_CONFIRMATION,
|
||||||
email
|
email,
|
||||||
});
|
});
|
||||||
|
|
||||||
// send mail
|
// send mail
|
||||||
await sendMail({
|
await sendMail({
|
||||||
template: 'emailVerification.handlebars',
|
template: "emailVerification.handlebars",
|
||||||
subjectLine: 'Infisical confirmation code',
|
subjectLine: "Infisical confirmation code",
|
||||||
recipients: [email],
|
recipients: [email],
|
||||||
substitutions: {
|
substitutions: {
|
||||||
code: token
|
code: token,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ export const sendEmailVerification = async ({ email }: { email: string }) => {
|
|||||||
*/
|
*/
|
||||||
export const checkEmailVerification = async ({
|
export const checkEmailVerification = async ({
|
||||||
email,
|
email,
|
||||||
code
|
code,
|
||||||
}: {
|
}: {
|
||||||
email: string;
|
email: string;
|
||||||
code: string;
|
code: string;
|
||||||
@@ -46,7 +46,7 @@ export const checkEmailVerification = async ({
|
|||||||
await TokenService.validateToken({
|
await TokenService.validateToken({
|
||||||
type: TOKEN_EMAIL_CONFIRMATION,
|
type: TOKEN_EMAIL_CONFIRMATION,
|
||||||
email,
|
email,
|
||||||
token: code
|
token: code,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ export const checkEmailVerification = async ({
|
|||||||
*/
|
*/
|
||||||
export const initializeDefaultOrg = async ({
|
export const initializeDefaultOrg = async ({
|
||||||
organizationName,
|
organizationName,
|
||||||
user
|
user,
|
||||||
}: {
|
}: {
|
||||||
organizationName: string;
|
organizationName: string;
|
||||||
user: IUser;
|
user: IUser;
|
||||||
@@ -69,14 +69,14 @@ export const initializeDefaultOrg = async ({
|
|||||||
// subscription
|
// subscription
|
||||||
const organization = await createOrganization({
|
const organization = await createOrganization({
|
||||||
email: user.email,
|
email: user.email,
|
||||||
name: organizationName
|
name: organizationName,
|
||||||
});
|
});
|
||||||
|
|
||||||
await addMembershipsOrg({
|
await addMembershipsOrg({
|
||||||
userIds: [user._id.toString()],
|
userIds: [user._id.toString()],
|
||||||
organizationId: organization._id.toString(),
|
organizationId: organization._id.toString(),
|
||||||
roles: [OWNER],
|
roles: [OWNER],
|
||||||
statuses: [ACCEPTED]
|
statuses: [ACCEPTED],
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new Error(`Failed to initialize default organization and workspace [err=${err}]`);
|
throw new Error(`Failed to initialize default organization and workspace [err=${err}]`);
|
||||||
|
|||||||
+12
-12
@@ -1,8 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
IUser,
|
IUser,
|
||||||
User,
|
User,
|
||||||
} from '../models';
|
} from "../models";
|
||||||
import { sendMail } from './nodemailer';
|
import { sendMail } from "./nodemailer";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize a user under email [email]
|
* Initialize a user under email [email]
|
||||||
@@ -12,7 +12,7 @@ import { sendMail } from './nodemailer';
|
|||||||
*/
|
*/
|
||||||
export const setupAccount = async ({ email }: { email: string }) => {
|
export const setupAccount = async ({ email }: { email: string }) => {
|
||||||
const user = await new User({
|
const user = await new User({
|
||||||
email
|
email,
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
@@ -49,7 +49,7 @@ export const completeAccount = async ({
|
|||||||
encryptedPrivateKeyIV,
|
encryptedPrivateKeyIV,
|
||||||
encryptedPrivateKeyTag,
|
encryptedPrivateKeyTag,
|
||||||
salt,
|
salt,
|
||||||
verifier
|
verifier,
|
||||||
}: {
|
}: {
|
||||||
userId: string;
|
userId: string;
|
||||||
firstName: string;
|
firstName: string;
|
||||||
@@ -66,7 +66,7 @@ export const completeAccount = async ({
|
|||||||
verifier: string;
|
verifier: string;
|
||||||
}) => {
|
}) => {
|
||||||
const options = {
|
const options = {
|
||||||
new: true
|
new: true,
|
||||||
};
|
};
|
||||||
const user = await User.findByIdAndUpdate(
|
const user = await User.findByIdAndUpdate(
|
||||||
userId,
|
userId,
|
||||||
@@ -82,7 +82,7 @@ export const completeAccount = async ({
|
|||||||
iv: encryptedPrivateKeyIV,
|
iv: encryptedPrivateKeyIV,
|
||||||
tag: encryptedPrivateKeyTag,
|
tag: encryptedPrivateKeyTag,
|
||||||
salt,
|
salt,
|
||||||
verifier
|
verifier,
|
||||||
},
|
},
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
@@ -100,7 +100,7 @@ export const completeAccount = async ({
|
|||||||
export const checkUserDevice = async ({
|
export const checkUserDevice = async ({
|
||||||
user,
|
user,
|
||||||
ip,
|
ip,
|
||||||
userAgent
|
userAgent,
|
||||||
}: {
|
}: {
|
||||||
user: IUser;
|
user: IUser;
|
||||||
ip: string;
|
ip: string;
|
||||||
@@ -114,22 +114,22 @@ export const checkUserDevice = async ({
|
|||||||
|
|
||||||
user.devices = user.devices.concat([{
|
user.devices = user.devices.concat([{
|
||||||
ip: String(ip),
|
ip: String(ip),
|
||||||
userAgent
|
userAgent,
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
await user.save();
|
await user.save();
|
||||||
|
|
||||||
// send MFA code [code] to [email]
|
// send MFA code [code] to [email]
|
||||||
await sendMail({
|
await sendMail({
|
||||||
template: 'newDevice.handlebars',
|
template: "newDevice.handlebars",
|
||||||
subjectLine: `Successful login from new device`,
|
subjectLine: "Successful login from new device",
|
||||||
recipients: [user.email],
|
recipients: [user.email],
|
||||||
substitutions: {
|
substitutions: {
|
||||||
email: user.email,
|
email: user.email,
|
||||||
timestamp: new Date().toString(),
|
timestamp: new Date().toString(),
|
||||||
ip,
|
ip,
|
||||||
userAgent
|
userAgent,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
import {
|
import {
|
||||||
Workspace,
|
|
||||||
Bot,
|
Bot,
|
||||||
Membership,
|
|
||||||
Key,
|
Key,
|
||||||
Secret
|
Membership,
|
||||||
} from '../models';
|
Secret,
|
||||||
import { createBot } from '../helpers/bot';
|
Workspace,
|
||||||
import { EELicenseService } from '../ee/services';
|
} from "../models";
|
||||||
import { SecretService } from '../services';
|
import { createBot } from "../helpers/bot";
|
||||||
|
import { EELicenseService } from "../ee/services";
|
||||||
|
import { SecretService } from "../services";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a workspace with name [name] in organization with id [organizationId]
|
* Create a workspace with name [name] in organization with id [organizationId]
|
||||||
@@ -18,7 +18,7 @@ import { SecretService } from '../services';
|
|||||||
*/
|
*/
|
||||||
export const createWorkspace = async ({
|
export const createWorkspace = async ({
|
||||||
name,
|
name,
|
||||||
organizationId
|
organizationId,
|
||||||
}: {
|
}: {
|
||||||
name: string;
|
name: string;
|
||||||
organizationId: string;
|
organizationId: string;
|
||||||
@@ -27,18 +27,18 @@ export const createWorkspace = async ({
|
|||||||
const workspace = await new Workspace({
|
const workspace = await new Workspace({
|
||||||
name,
|
name,
|
||||||
organization: organizationId,
|
organization: organizationId,
|
||||||
autoCapitalization: true
|
autoCapitalization: true,
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
// initialize bot for workspace
|
// initialize bot for workspace
|
||||||
await createBot({
|
await createBot({
|
||||||
name: 'Infisical Bot',
|
name: "Infisical Bot",
|
||||||
workspaceId: workspace._id
|
workspaceId: workspace._id,
|
||||||
});
|
});
|
||||||
|
|
||||||
// initialize blind index salt for workspace
|
// initialize blind index salt for workspace
|
||||||
await SecretService.createSecretBlindIndexData({
|
await SecretService.createSecretBlindIndexData({
|
||||||
workspaceId: workspace._id
|
workspaceId: workspace._id,
|
||||||
});
|
});
|
||||||
|
|
||||||
await EELicenseService.refreshPlan(organizationId);
|
await EELicenseService.refreshPlan(organizationId);
|
||||||
@@ -55,15 +55,15 @@ export const createWorkspace = async ({
|
|||||||
export const deleteWorkspace = async ({ id }: { id: string }) => {
|
export const deleteWorkspace = async ({ id }: { id: string }) => {
|
||||||
await Workspace.deleteOne({ _id: id });
|
await Workspace.deleteOne({ _id: id });
|
||||||
await Bot.deleteOne({
|
await Bot.deleteOne({
|
||||||
workspace: id
|
workspace: id,
|
||||||
});
|
});
|
||||||
await Membership.deleteMany({
|
await Membership.deleteMany({
|
||||||
workspace: id
|
workspace: id,
|
||||||
});
|
});
|
||||||
await Secret.deleteMany({
|
await Secret.deleteMany({
|
||||||
workspace: id
|
workspace: id,
|
||||||
});
|
});
|
||||||
await Key.deleteMany({
|
await Key.deleteMany({
|
||||||
workspace: id
|
workspace: id,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
+18
-18
@@ -2,7 +2,7 @@ import dotenv from "dotenv";
|
|||||||
dotenv.config();
|
dotenv.config();
|
||||||
import express from "express";
|
import express from "express";
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
require('express-async-errors');
|
require("express-async-errors");
|
||||||
import helmet from "helmet";
|
import helmet from "helmet";
|
||||||
import cors from "cors";
|
import cors from "cors";
|
||||||
import { DatabaseService } from "./services";
|
import { DatabaseService } from "./services";
|
||||||
@@ -15,32 +15,32 @@ const swaggerFile = require("../spec.json");
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
import { apiLimiter } from "./helpers/rateLimiter";
|
import { apiLimiter } from "./helpers/rateLimiter";
|
||||||
import {
|
import {
|
||||||
workspace as eeWorkspaceRouter,
|
action as eeActionRouter,
|
||||||
|
cloudProducts as eeCloudProductsRouter,
|
||||||
|
organizations as eeOrganizationsRouter,
|
||||||
secret as eeSecretRouter,
|
secret as eeSecretRouter,
|
||||||
secretSnapshot as eeSecretSnapshotRouter,
|
secretSnapshot as eeSecretSnapshotRouter,
|
||||||
action as eeActionRouter,
|
workspace as eeWorkspaceRouter,
|
||||||
organizations as eeOrganizationsRouter,
|
|
||||||
cloudProducts as eeCloudProductsRouter,
|
|
||||||
} from "./ee/routes/v1";
|
} from "./ee/routes/v1";
|
||||||
import {
|
import {
|
||||||
signup as v1SignupRouter,
|
|
||||||
auth as v1AuthRouter,
|
auth as v1AuthRouter,
|
||||||
bot as v1BotRouter,
|
bot as v1BotRouter,
|
||||||
organization as v1OrganizationRouter,
|
integrationAuth as v1IntegrationAuthRouter,
|
||||||
workspace as v1WorkspaceRouter,
|
integration as v1IntegrationRouter,
|
||||||
|
inviteOrg as v1InviteOrgRouter,
|
||||||
|
key as v1KeyRouter,
|
||||||
membershipOrg as v1MembershipOrgRouter,
|
membershipOrg as v1MembershipOrgRouter,
|
||||||
membership as v1MembershipRouter,
|
membership as v1MembershipRouter,
|
||||||
key as v1KeyRouter,
|
organization as v1OrganizationRouter,
|
||||||
inviteOrg as v1InviteOrgRouter,
|
|
||||||
user as v1UserRouter,
|
|
||||||
userAction as v1UserActionRouter,
|
|
||||||
secret as v1SecretRouter,
|
|
||||||
serviceToken as v1ServiceTokenRouter,
|
|
||||||
password as v1PasswordRouter,
|
password as v1PasswordRouter,
|
||||||
stripe as v1StripeRouter,
|
secret as v1SecretRouter,
|
||||||
integration as v1IntegrationRouter,
|
|
||||||
integrationAuth as v1IntegrationAuthRouter,
|
|
||||||
secretsFolder as v1SecretsFolder,
|
secretsFolder as v1SecretsFolder,
|
||||||
|
serviceToken as v1ServiceTokenRouter,
|
||||||
|
signup as v1SignupRouter,
|
||||||
|
stripe as v1StripeRouter,
|
||||||
|
userAction as v1UserActionRouter,
|
||||||
|
user as v1UserRouter,
|
||||||
|
workspace as v1WorkspaceRouter,
|
||||||
} from "./routes/v1";
|
} from "./routes/v1";
|
||||||
import {
|
import {
|
||||||
signup as v2SignupRouter,
|
signup as v2SignupRouter,
|
||||||
@@ -95,7 +95,7 @@ const main = async () => {
|
|||||||
|
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
// default to IP address provided by Cloudflare
|
// default to IP address provided by Cloudflare
|
||||||
const cfIp = req.headers['cf-connecting-ip'];
|
const cfIp = req.headers["cf-connecting-ip"];
|
||||||
req.realIP = Array.isArray(cfIp) ? cfIp[0] : (cfIp as string) || req.ip;
|
req.realIP = Array.isArray(cfIp) ? cfIp[0] : (cfIp as string) || req.ip;
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,32 +2,32 @@ import { Octokit } from "@octokit/rest";
|
|||||||
import { IIntegrationAuth } from "../models";
|
import { IIntegrationAuth } from "../models";
|
||||||
import { standardRequest } from "../config/request";
|
import { standardRequest } from "../config/request";
|
||||||
import {
|
import {
|
||||||
INTEGRATION_AZURE_KEY_VAULT,
|
|
||||||
INTEGRATION_AWS_PARAMETER_STORE,
|
INTEGRATION_AWS_PARAMETER_STORE,
|
||||||
INTEGRATION_AWS_SECRET_MANAGER,
|
INTEGRATION_AWS_SECRET_MANAGER,
|
||||||
INTEGRATION_HEROKU,
|
INTEGRATION_AZURE_KEY_VAULT,
|
||||||
INTEGRATION_VERCEL,
|
INTEGRATION_CHECKLY,
|
||||||
INTEGRATION_NETLIFY,
|
INTEGRATION_CHECKLY_API_URL,
|
||||||
|
INTEGRATION_CIRCLECI,
|
||||||
|
INTEGRATION_CIRCLECI_API_URL,
|
||||||
|
INTEGRATION_FLYIO,
|
||||||
|
INTEGRATION_FLYIO_API_URL,
|
||||||
INTEGRATION_GITHUB,
|
INTEGRATION_GITHUB,
|
||||||
INTEGRATION_GITLAB,
|
INTEGRATION_GITLAB,
|
||||||
INTEGRATION_RENDER,
|
|
||||||
INTEGRATION_RAILWAY,
|
|
||||||
INTEGRATION_FLYIO,
|
|
||||||
INTEGRATION_CIRCLECI,
|
|
||||||
INTEGRATION_TRAVISCI,
|
|
||||||
INTEGRATION_SUPABASE,
|
|
||||||
INTEGRATION_CHECKLY,
|
|
||||||
INTEGRATION_HEROKU_API_URL,
|
|
||||||
INTEGRATION_GITLAB_API_URL,
|
INTEGRATION_GITLAB_API_URL,
|
||||||
INTEGRATION_VERCEL_API_URL,
|
INTEGRATION_HEROKU,
|
||||||
|
INTEGRATION_HEROKU_API_URL,
|
||||||
|
INTEGRATION_NETLIFY,
|
||||||
INTEGRATION_NETLIFY_API_URL,
|
INTEGRATION_NETLIFY_API_URL,
|
||||||
INTEGRATION_RENDER_API_URL,
|
INTEGRATION_RAILWAY,
|
||||||
INTEGRATION_RAILWAY_API_URL,
|
INTEGRATION_RAILWAY_API_URL,
|
||||||
INTEGRATION_FLYIO_API_URL,
|
INTEGRATION_RENDER,
|
||||||
INTEGRATION_CIRCLECI_API_URL,
|
INTEGRATION_RENDER_API_URL,
|
||||||
INTEGRATION_TRAVISCI_API_URL,
|
INTEGRATION_SUPABASE,
|
||||||
INTEGRATION_SUPABASE_API_URL,
|
INTEGRATION_SUPABASE_API_URL,
|
||||||
INTEGRATION_CHECKLY_API_URL
|
INTEGRATION_TRAVISCI,
|
||||||
|
INTEGRATION_TRAVISCI_API_URL,
|
||||||
|
INTEGRATION_VERCEL,
|
||||||
|
INTEGRATION_VERCEL_API_URL,
|
||||||
} from "../variables";
|
} from "../variables";
|
||||||
|
|
||||||
interface App {
|
interface App {
|
||||||
@@ -214,7 +214,7 @@ const getAppsNetlify = async ({ accessToken }: { accessToken: string }) => {
|
|||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
page: String(page),
|
page: String(page),
|
||||||
per_page: String(perPage),
|
per_page: String(perPage),
|
||||||
filter: 'all'
|
filter: "all",
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data } = await standardRequest.get(
|
const { data } = await standardRequest.get(
|
||||||
|
|||||||
@@ -1,31 +1,31 @@
|
|||||||
import { standardRequest } from "../config/request";
|
import { standardRequest } from "../config/request";
|
||||||
import {
|
import {
|
||||||
INTEGRATION_AZURE_KEY_VAULT,
|
INTEGRATION_AZURE_KEY_VAULT,
|
||||||
INTEGRATION_HEROKU,
|
|
||||||
INTEGRATION_VERCEL,
|
|
||||||
INTEGRATION_NETLIFY,
|
|
||||||
INTEGRATION_GITHUB,
|
|
||||||
INTEGRATION_GITLAB,
|
|
||||||
INTEGRATION_AZURE_TOKEN_URL,
|
INTEGRATION_AZURE_TOKEN_URL,
|
||||||
INTEGRATION_HEROKU_TOKEN_URL,
|
INTEGRATION_GITHUB,
|
||||||
INTEGRATION_VERCEL_TOKEN_URL,
|
|
||||||
INTEGRATION_NETLIFY_TOKEN_URL,
|
|
||||||
INTEGRATION_GITHUB_TOKEN_URL,
|
INTEGRATION_GITHUB_TOKEN_URL,
|
||||||
|
INTEGRATION_GITLAB,
|
||||||
INTEGRATION_GITLAB_TOKEN_URL,
|
INTEGRATION_GITLAB_TOKEN_URL,
|
||||||
|
INTEGRATION_HEROKU,
|
||||||
|
INTEGRATION_HEROKU_TOKEN_URL,
|
||||||
|
INTEGRATION_NETLIFY,
|
||||||
|
INTEGRATION_NETLIFY_TOKEN_URL,
|
||||||
|
INTEGRATION_VERCEL,
|
||||||
|
INTEGRATION_VERCEL_TOKEN_URL,
|
||||||
} from "../variables";
|
} from "../variables";
|
||||||
import {
|
import {
|
||||||
getSiteURL,
|
|
||||||
getClientIdAzure,
|
getClientIdAzure,
|
||||||
getClientSecretAzure,
|
|
||||||
getClientSecretHeroku,
|
|
||||||
getClientIdVercel,
|
|
||||||
getClientSecretVercel,
|
|
||||||
getClientIdNetlify,
|
|
||||||
getClientSecretNetlify,
|
|
||||||
getClientIdGitHub,
|
getClientIdGitHub,
|
||||||
getClientSecretGitHub,
|
|
||||||
getClientIdGitLab,
|
getClientIdGitLab,
|
||||||
|
getClientIdNetlify,
|
||||||
|
getClientIdVercel,
|
||||||
|
getClientSecretAzure,
|
||||||
|
getClientSecretGitHub,
|
||||||
getClientSecretGitLab,
|
getClientSecretGitLab,
|
||||||
|
getClientSecretHeroku,
|
||||||
|
getClientSecretNetlify,
|
||||||
|
getClientSecretVercel,
|
||||||
|
getSiteURL,
|
||||||
} from "../config";
|
} from "../config";
|
||||||
|
|
||||||
interface ExchangeCodeAzureResponse {
|
interface ExchangeCodeAzureResponse {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { exchangeCode } from './exchange';
|
import { exchangeCode } from "./exchange";
|
||||||
import { exchangeRefresh } from './refresh';
|
import { exchangeRefresh } from "./refresh";
|
||||||
import { getApps } from './apps';
|
import { getApps } from "./apps";
|
||||||
import { getTeams } from './teams';
|
import { getTeams } from "./teams";
|
||||||
import { syncSecrets } from './sync';
|
import { syncSecrets } from "./sync";
|
||||||
import { revokeAccess } from './revoke';
|
import { revokeAccess } from "./revoke";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
exchangeCode,
|
exchangeCode,
|
||||||
@@ -11,5 +11,5 @@ export {
|
|||||||
getApps,
|
getApps,
|
||||||
getTeams,
|
getTeams,
|
||||||
syncSecrets,
|
syncSecrets,
|
||||||
revokeAccess
|
revokeAccess,
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user