mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 07:35:09 +00:00
feat(onboarding): backend api for onboarding users
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
import ora from "ora";
|
||||
import nodemailer from "nodemailer";
|
||||
import { getSmtpHost, getSmtpPort } from "./config";
|
||||
import { logger } from "./utils/logging";
|
||||
import mongoose from "mongoose";
|
||||
import { redisClient } from "./services/RedisService";
|
||||
|
||||
type BootstrapOpt = {
|
||||
transporter: nodemailer.Transporter;
|
||||
};
|
||||
|
||||
export const bootstrap = async ({ transporter }: BootstrapOpt) => {
|
||||
const spinner = ora().start();
|
||||
spinner.info("Checking configurations...");
|
||||
spinner.info("Testing smtp connection");
|
||||
|
||||
await transporter
|
||||
.verify()
|
||||
.then(async () => {
|
||||
spinner.succeed("SMTP successfully connected");
|
||||
})
|
||||
.catch(async (err) => {
|
||||
spinner.fail(`SMTP - Failed to connect to ${await getSmtpHost()}:${await getSmtpPort()}`);
|
||||
logger.error(err);
|
||||
});
|
||||
|
||||
spinner.info("Testing mongodb connection");
|
||||
if (mongoose.connection.readyState !== mongoose.ConnectionStates.connected) {
|
||||
spinner.fail("Mongo DB - Failed to connect");
|
||||
} else {
|
||||
spinner.succeed("Mongodb successfully connected");
|
||||
}
|
||||
|
||||
spinner.info("Testing redis connection");
|
||||
const redisPing = await redisClient?.ping();
|
||||
if (!redisPing) {
|
||||
spinner.fail("Redis - Failed to connect");
|
||||
} else {
|
||||
spinner.succeed("Redis successfully connected");
|
||||
}
|
||||
|
||||
spinner.stop();
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
import { IServerConfig, ServerConfig } from "../models/serverConfig";
|
||||
|
||||
let serverConfig: IServerConfig;
|
||||
|
||||
export const serverConfigInit = async () => {
|
||||
const cfg = await ServerConfig.findOne({});
|
||||
if (!cfg) {
|
||||
const cfg = new ServerConfig();
|
||||
await cfg.save();
|
||||
serverConfig = cfg;
|
||||
} else {
|
||||
serverConfig = cfg;
|
||||
}
|
||||
return serverConfig;
|
||||
};
|
||||
|
||||
export const getServerConfig = () => serverConfig;
|
||||
|
||||
export const updateServerConfig = async (data: Partial<IServerConfig>) => {
|
||||
const cfg = await ServerConfig.findByIdAndUpdate(serverConfig._id, data, { new: true });
|
||||
if (!cfg) throw new Error("Failed to update server config");
|
||||
serverConfig = cfg;
|
||||
return serverConfig;
|
||||
};
|
||||
@@ -0,0 +1,87 @@
|
||||
import { Request, Response } from "express";
|
||||
import { getHttpsEnabled } from "../../config";
|
||||
import { getServerConfig, updateServerConfig as setServerConfig } from "../../config/serverConfig";
|
||||
import { initializeDefaultOrg, issueAuthTokens } from "../../helpers";
|
||||
import { validateRequest } from "../../helpers/validation";
|
||||
import { User } from "../../models";
|
||||
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
|
||||
import * as reqValidator from "../../validation/admin";
|
||||
|
||||
export const getServerConfigInfo = (_req: Request, res: Response) => {
|
||||
const config = getServerConfig();
|
||||
return res.send({ config });
|
||||
};
|
||||
|
||||
export const updateServerConfig = async (req: Request, res: Response) => {
|
||||
const {
|
||||
body: { allowSignUp }
|
||||
} = await validateRequest(reqValidator.UpdateServerConfigV1, req);
|
||||
const config = await setServerConfig({ allowSignUp });
|
||||
return res.send({ config });
|
||||
};
|
||||
|
||||
export const adminSignUp = async (req: Request, res: Response) => {
|
||||
const cfg = getServerConfig();
|
||||
if (cfg.initialized) throw UnauthorizedRequestError({ message: "Admin has been created" });
|
||||
const {
|
||||
body: {
|
||||
email,
|
||||
publicKey,
|
||||
salt,
|
||||
lastName,
|
||||
verifier,
|
||||
firstName,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag,
|
||||
encryptedPrivateKey,
|
||||
encryptedPrivateKeyIV,
|
||||
encryptedPrivateKeyTag
|
||||
}
|
||||
} = await validateRequest(reqValidator.SignupV1, req);
|
||||
let user = await User.findOne({ email });
|
||||
if (user) throw BadRequestError({ message: "User already exist" });
|
||||
user = new User({
|
||||
email,
|
||||
firstName,
|
||||
lastName,
|
||||
encryptionVersion: 2,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag,
|
||||
publicKey,
|
||||
encryptedPrivateKey,
|
||||
iv: encryptedPrivateKeyIV,
|
||||
tag: encryptedPrivateKeyTag,
|
||||
salt,
|
||||
verifier,
|
||||
superAdmin: true
|
||||
});
|
||||
await user.save();
|
||||
await initializeDefaultOrg({ organizationName: "Admin Org", user });
|
||||
|
||||
await setServerConfig({ initialized: true });
|
||||
|
||||
// issue tokens
|
||||
const tokens = await issueAuthTokens({
|
||||
userId: user._id,
|
||||
ip: req.realIP,
|
||||
userAgent: req.headers["user-agent"] ?? ""
|
||||
});
|
||||
|
||||
const token = tokens.token;
|
||||
|
||||
// store (refresh) token in httpOnly cookie
|
||||
res.cookie("jid", tokens.refreshToken, {
|
||||
httpOnly: true,
|
||||
path: "/",
|
||||
sameSite: "strict",
|
||||
secure: await getHttpsEnabled()
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
message: "Successfully set up admin account",
|
||||
user,
|
||||
token
|
||||
});
|
||||
};
|
||||
@@ -16,6 +16,8 @@ import * as workspaceController from "./workspaceController";
|
||||
import * as secretScanningController from "./secretScanningController";
|
||||
import * as webhookController from "./webhookController";
|
||||
import * as secretImpsController from "./secretImpsController";
|
||||
import * as adminController from "./adminController";
|
||||
|
||||
export {
|
||||
authController,
|
||||
botController,
|
||||
@@ -34,5 +36,6 @@ export {
|
||||
workspaceController,
|
||||
secretScanningController,
|
||||
webhookController,
|
||||
secretImpsController
|
||||
secretImpsController,
|
||||
adminController
|
||||
};
|
||||
|
||||
+20
-1
@@ -35,6 +35,7 @@ import {
|
||||
import { apiKeyData as v3apiKeyDataRouter } from "./ee/routes/v3";
|
||||
import { serviceTokenData as v3ServiceTokenDataRouter } from "./ee/routes/v3";
|
||||
import {
|
||||
admin as v1AdminRouter,
|
||||
auth as v1AuthRouter,
|
||||
bot as v1BotRouter,
|
||||
integrationAuth as v1IntegrationAuthRouter,
|
||||
@@ -94,6 +95,7 @@ import { syncSecretsToThirdPartyServices } from "./queues/integrations/syncSecre
|
||||
import { githubPushEventSecretScan } from "./queues/secret-scanning/githubScanPushEvent";
|
||||
const SmeeClient = require("smee-client"); // eslint-disable-line
|
||||
import path from "path";
|
||||
import { serverConfigInit } from "./config/serverConfig";
|
||||
|
||||
let handler: null | any = null;
|
||||
|
||||
@@ -101,6 +103,7 @@ const main = async () => {
|
||||
const port = await getPort();
|
||||
|
||||
await setup();
|
||||
const serverCfg = await serverConfigInit();
|
||||
|
||||
await EELicenseService.initGlobalFeatureSet();
|
||||
|
||||
@@ -203,6 +206,7 @@ const main = async () => {
|
||||
// v1 routes
|
||||
app.use("/api/v1/signup", v1SignupRouter);
|
||||
app.use("/api/v1/auth", v1AuthRouter);
|
||||
app.use("/api/v1/admin", v1AdminRouter);
|
||||
app.use("/api/v1/bot", v1BotRouter);
|
||||
app.use("/api/v1/user", v1UserRouter);
|
||||
app.use("/api/v1/user-action", v1UserActionRouter);
|
||||
@@ -271,7 +275,22 @@ const main = async () => {
|
||||
app.use(requestErrorHandler);
|
||||
|
||||
const server = app.listen(port, async () => {
|
||||
logger.info(`Server started listening at port ${port}`);
|
||||
if (!serverCfg.initialized) {
|
||||
logger.info(`Welcome to Infisical
|
||||
|
||||
Create your Infisical administrator account at:
|
||||
http://localhost:${port}/admin/signup
|
||||
`);
|
||||
} else {
|
||||
logger.info(`Welcome back!
|
||||
|
||||
To access Infisical Administrator Panel open
|
||||
http://localhost:${port}/admin
|
||||
|
||||
To access Infisical server
|
||||
http://localhost:${port}
|
||||
`);
|
||||
}
|
||||
});
|
||||
|
||||
// await createTestUserForDevelopment();
|
||||
|
||||
@@ -7,17 +7,21 @@ import requireSecretAuth from "./requireSecretAuth";
|
||||
import requireSecretsAuth from "./requireSecretsAuth";
|
||||
import requireBlindIndicesEnabled from "./requireBlindIndicesEnabled";
|
||||
import requireE2EEOff from "./requireE2EEOff";
|
||||
import { requireSuperAdminAccess } from "./requireSuperAdminAccess";
|
||||
import validateRequest from "./validateRequest";
|
||||
import { disableSignUpByServerCfg } from "./serverAdmin";
|
||||
|
||||
export {
|
||||
requireAuth,
|
||||
requireMfaAuth,
|
||||
requireSignupAuth,
|
||||
requireWorkspaceAuth,
|
||||
requireServiceTokenAuth,
|
||||
requireSecretAuth,
|
||||
requireSecretsAuth,
|
||||
requireBlindIndicesEnabled,
|
||||
requireE2EEOff,
|
||||
validateRequest,
|
||||
requireAuth,
|
||||
requireMfaAuth,
|
||||
requireSignupAuth,
|
||||
requireWorkspaceAuth,
|
||||
requireServiceTokenAuth,
|
||||
requireSecretAuth,
|
||||
requireSecretsAuth,
|
||||
requireBlindIndicesEnabled,
|
||||
requireE2EEOff,
|
||||
validateRequest,
|
||||
requireSuperAdminAccess,
|
||||
disableSignUpByServerCfg
|
||||
};
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { NextFunction, Request, Response } from "express";
|
||||
import { UnauthorizedRequestError } from "../utils/errors";
|
||||
|
||||
export const requireSuperAdminAccess = (req: Request, _res: Response, next: NextFunction) => {
|
||||
const isSuperAdmin = req.user.superAdmin;
|
||||
if (!isSuperAdmin) throw UnauthorizedRequestError({ message: "Requires superadmin access" });
|
||||
return next();
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
import { NextFunction, Request, Response } from "express";
|
||||
import { getServerConfig } from "../config/serverConfig";
|
||||
import { BadRequestError } from "../utils/errors";
|
||||
|
||||
export const disableSignUpByServerCfg = (_req: Request, _res: Response, next: NextFunction) => {
|
||||
const cfg = getServerConfig();
|
||||
if (!cfg.allowSignUp) throw BadRequestError({ message: "Signup are disabled" });
|
||||
return next();
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Schema, Types, model } from "mongoose";
|
||||
|
||||
export interface IServerConfig {
|
||||
_id: Types.ObjectId;
|
||||
initialized: boolean;
|
||||
allowSignUp: boolean;
|
||||
}
|
||||
|
||||
const serverConfigSchema = new Schema<IServerConfig>(
|
||||
{
|
||||
initialized: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
allowSignUp: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
{
|
||||
timestamps: true
|
||||
}
|
||||
);
|
||||
|
||||
export const ServerConfig = model<IServerConfig>("ServerConfig", serverConfigSchema);
|
||||
+130
-114
@@ -1,125 +1,141 @@
|
||||
import { Document, Schema, Types, model } from "mongoose";
|
||||
|
||||
export enum AuthMethod {
|
||||
EMAIL = "email",
|
||||
GOOGLE = "google",
|
||||
GITHUB = "github",
|
||||
GITLAB = "gitlab",
|
||||
OKTA_SAML = "okta-saml",
|
||||
AZURE_SAML = "azure-saml",
|
||||
JUMPCLOUD_SAML = "jumpcloud-saml",
|
||||
EMAIL = "email",
|
||||
GOOGLE = "google",
|
||||
GITHUB = "github",
|
||||
GITLAB = "gitlab",
|
||||
OKTA_SAML = "okta-saml",
|
||||
AZURE_SAML = "azure-saml",
|
||||
JUMPCLOUD_SAML = "jumpcloud-saml"
|
||||
}
|
||||
|
||||
export interface IUser extends Document {
|
||||
_id: Types.ObjectId;
|
||||
authProvider?: AuthMethod;
|
||||
authMethods: AuthMethod[];
|
||||
email: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
encryptionVersion: number;
|
||||
protectedKey: string;
|
||||
protectedKeyIV: string;
|
||||
protectedKeyTag: string;
|
||||
publicKey?: string;
|
||||
encryptedPrivateKey?: string;
|
||||
iv?: string;
|
||||
tag?: string;
|
||||
salt?: string;
|
||||
verifier?: string;
|
||||
isMfaEnabled: boolean;
|
||||
mfaMethods: boolean;
|
||||
devices: {
|
||||
ip: string;
|
||||
userAgent: string;
|
||||
}[];
|
||||
_id: Types.ObjectId;
|
||||
authProvider?: AuthMethod;
|
||||
authMethods: AuthMethod[];
|
||||
email: string;
|
||||
superAdmin?: boolean;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
encryptionVersion: number;
|
||||
protectedKey: string;
|
||||
protectedKeyIV: string;
|
||||
protectedKeyTag: string;
|
||||
publicKey?: string;
|
||||
encryptedPrivateKey?: string;
|
||||
iv?: string;
|
||||
tag?: string;
|
||||
salt?: string;
|
||||
verifier?: string;
|
||||
isMfaEnabled: boolean;
|
||||
mfaMethods: boolean;
|
||||
devices: {
|
||||
ip: string;
|
||||
userAgent: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
const userSchema = new Schema<IUser>(
|
||||
{
|
||||
authProvider: { // TODO field: deprecate
|
||||
type: String,
|
||||
enum: AuthMethod,
|
||||
},
|
||||
authMethods: {
|
||||
type: [{
|
||||
type: String,
|
||||
enum: AuthMethod,
|
||||
}],
|
||||
default: [AuthMethod.EMAIL],
|
||||
required: true
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true,
|
||||
},
|
||||
firstName: {
|
||||
type: String,
|
||||
},
|
||||
lastName: {
|
||||
type: String,
|
||||
},
|
||||
encryptionVersion: {
|
||||
type: Number,
|
||||
select: false,
|
||||
default: 1, // to resolve backward-compatibility issues
|
||||
},
|
||||
protectedKey: { // introduced as part of encryption version 2
|
||||
type: String,
|
||||
select: false,
|
||||
},
|
||||
protectedKeyIV: { // introduced as part of encryption version 2
|
||||
type: String,
|
||||
select: false,
|
||||
},
|
||||
protectedKeyTag: { // introduced as part of encryption version 2
|
||||
type: String,
|
||||
select: false,
|
||||
},
|
||||
publicKey: {
|
||||
type: String,
|
||||
select: false,
|
||||
},
|
||||
encryptedPrivateKey: {
|
||||
type: String,
|
||||
select: false,
|
||||
},
|
||||
iv: { // iv of [encryptedPrivateKey]
|
||||
type: String,
|
||||
select: false,
|
||||
},
|
||||
tag: { // tag of [encryptedPrivateKey]
|
||||
type: String,
|
||||
select: false,
|
||||
},
|
||||
salt: {
|
||||
type: String,
|
||||
select: false,
|
||||
},
|
||||
verifier: {
|
||||
type: String,
|
||||
select: false,
|
||||
},
|
||||
isMfaEnabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
mfaMethods: [{
|
||||
type: String,
|
||||
}],
|
||||
devices: {
|
||||
type: [{
|
||||
ip: String,
|
||||
userAgent: String,
|
||||
}],
|
||||
default: [],
|
||||
select: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
}
|
||||
{
|
||||
authProvider: {
|
||||
// TODO field: deprecate
|
||||
type: String,
|
||||
enum: AuthMethod
|
||||
},
|
||||
authMethods: {
|
||||
type: [
|
||||
{
|
||||
type: String,
|
||||
enum: AuthMethod
|
||||
}
|
||||
],
|
||||
default: [AuthMethod.EMAIL],
|
||||
required: true
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true
|
||||
},
|
||||
firstName: {
|
||||
type: String
|
||||
},
|
||||
lastName: {
|
||||
type: String
|
||||
},
|
||||
encryptionVersion: {
|
||||
type: Number,
|
||||
select: false,
|
||||
default: 1 // to resolve backward-compatibility issues
|
||||
},
|
||||
protectedKey: {
|
||||
// introduced as part of encryption version 2
|
||||
type: String,
|
||||
select: false
|
||||
},
|
||||
protectedKeyIV: {
|
||||
// introduced as part of encryption version 2
|
||||
type: String,
|
||||
select: false
|
||||
},
|
||||
protectedKeyTag: {
|
||||
// introduced as part of encryption version 2
|
||||
type: String,
|
||||
select: false
|
||||
},
|
||||
publicKey: {
|
||||
type: String,
|
||||
select: false
|
||||
},
|
||||
encryptedPrivateKey: {
|
||||
type: String,
|
||||
select: false
|
||||
},
|
||||
superAdmin: {
|
||||
type: Boolean
|
||||
},
|
||||
iv: {
|
||||
// iv of [encryptedPrivateKey]
|
||||
type: String,
|
||||
select: false
|
||||
},
|
||||
tag: {
|
||||
// tag of [encryptedPrivateKey]
|
||||
type: String,
|
||||
select: false
|
||||
},
|
||||
salt: {
|
||||
type: String,
|
||||
select: false
|
||||
},
|
||||
verifier: {
|
||||
type: String,
|
||||
select: false
|
||||
},
|
||||
isMfaEnabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
mfaMethods: [
|
||||
{
|
||||
type: String
|
||||
}
|
||||
],
|
||||
devices: {
|
||||
type: [
|
||||
{
|
||||
ip: String,
|
||||
userAgent: String
|
||||
}
|
||||
],
|
||||
default: [],
|
||||
select: false
|
||||
}
|
||||
},
|
||||
{
|
||||
timestamps: true
|
||||
}
|
||||
);
|
||||
|
||||
export const User = model<IUser>("User", userSchema);
|
||||
export const User = model<IUser>("User", userSchema);
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import express from "express";
|
||||
import { adminController } from "../../controllers/v1";
|
||||
const router = express.Router();
|
||||
import { requireAuth, requireSuperAdminAccess } from "../../middleware";
|
||||
import { AuthMode } from "../../variables";
|
||||
|
||||
router.get("/config", adminController.getServerConfigInfo);
|
||||
|
||||
router.post("/signup", adminController.adminSignUp);
|
||||
|
||||
router.patch(
|
||||
"/config",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
}),
|
||||
requireSuperAdminAccess,
|
||||
adminController.updateServerConfig
|
||||
);
|
||||
|
||||
export default router;
|
||||
@@ -18,6 +18,7 @@ import integrationAuth from "./integrationAuth";
|
||||
import secretsFolder from "./secretsFolder";
|
||||
import webhooks from "./webhook";
|
||||
import secretImps from "./secretImps";
|
||||
import admin from "./admin";
|
||||
|
||||
export {
|
||||
signup,
|
||||
@@ -39,5 +40,6 @@ export {
|
||||
secretsFolder,
|
||||
webhooks,
|
||||
secretImps,
|
||||
sso
|
||||
sso,
|
||||
admin
|
||||
};
|
||||
|
||||
@@ -2,18 +2,21 @@ import express from "express";
|
||||
const router = express.Router();
|
||||
import { signupController } from "../../controllers/v1";
|
||||
import { authLimiter } from "../../helpers/rateLimiter";
|
||||
import { disableSignUpByServerCfg } from "../../middleware";
|
||||
|
||||
// TODO: consider moving to users/v3/signup
|
||||
|
||||
router.post(
|
||||
// TODO endpoint: consider moving to v3/users/signup/mail
|
||||
"/email/signup",
|
||||
disableSignUpByServerCfg,
|
||||
authLimiter,
|
||||
signupController.beginEmailSignup
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/email/verify", // TODO endpoint: consider moving to v3/users/signup/verify
|
||||
disableSignUpByServerCfg,
|
||||
authLimiter,
|
||||
signupController.verifyEmailSignup
|
||||
);
|
||||
|
||||
@@ -1,49 +1,51 @@
|
||||
import express from "express";
|
||||
const router = express.Router();
|
||||
import { body } from "express-validator";
|
||||
import { requireSignupAuth, validateRequest } from "../../middleware";
|
||||
import { disableSignUpByServerCfg, requireSignupAuth, validateRequest } from "../../middleware";
|
||||
import { signupController } from "../../controllers/v2";
|
||||
import { authLimiter } from "../../helpers/rateLimiter";
|
||||
|
||||
router.post(
|
||||
"/complete-account/signup", // TODO endpoint: deprecate (moved to v3/signup/complete/account-signup)
|
||||
authLimiter,
|
||||
requireSignupAuth,
|
||||
body("email").exists().isString().trim().notEmpty().isEmail(),
|
||||
body("firstName").exists().isString().trim().notEmpty(),
|
||||
body("lastName").exists().isString().trim().notEmpty(),
|
||||
body("protectedKey").exists().isString().trim().notEmpty(),
|
||||
body("protectedKeyIV").exists().isString().trim().notEmpty(),
|
||||
body("protectedKeyTag").exists().isString().trim().notEmpty(),
|
||||
body("publicKey").exists().isString().trim().notEmpty(),
|
||||
body("encryptedPrivateKey").exists().isString().trim().notEmpty(),
|
||||
body("encryptedPrivateKeyIV").exists().isString().trim().notEmpty(),
|
||||
body("encryptedPrivateKeyTag").exists().isString().trim().notEmpty(),
|
||||
body("salt").exists().isString().trim().notEmpty(),
|
||||
body("verifier").exists().isString().trim().notEmpty(),
|
||||
body("organizationName").exists().isString().trim().notEmpty(),
|
||||
validateRequest,
|
||||
signupController.completeAccountSignup
|
||||
"/complete-account/signup", // TODO endpoint: deprecate (moved to v3/signup/complete/account-signup),
|
||||
disableSignUpByServerCfg,
|
||||
authLimiter,
|
||||
requireSignupAuth,
|
||||
body("email").exists().isString().trim().notEmpty().isEmail(),
|
||||
body("firstName").exists().isString().trim().notEmpty(),
|
||||
body("lastName").exists().isString().trim().notEmpty(),
|
||||
body("protectedKey").exists().isString().trim().notEmpty(),
|
||||
body("protectedKeyIV").exists().isString().trim().notEmpty(),
|
||||
body("protectedKeyTag").exists().isString().trim().notEmpty(),
|
||||
body("publicKey").exists().isString().trim().notEmpty(),
|
||||
body("encryptedPrivateKey").exists().isString().trim().notEmpty(),
|
||||
body("encryptedPrivateKeyIV").exists().isString().trim().notEmpty(),
|
||||
body("encryptedPrivateKeyTag").exists().isString().trim().notEmpty(),
|
||||
body("salt").exists().isString().trim().notEmpty(),
|
||||
body("verifier").exists().isString().trim().notEmpty(),
|
||||
body("organizationName").exists().isString().trim().notEmpty(),
|
||||
validateRequest,
|
||||
signupController.completeAccountSignup
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/complete-account/invite", // TODO: consider moving to v3/users/new/complete-account/invite
|
||||
authLimiter,
|
||||
requireSignupAuth,
|
||||
body("email").exists().isString().trim().notEmpty().isEmail(),
|
||||
body("firstName").exists().isString().trim().notEmpty(),
|
||||
body("lastName").exists().isString().trim().notEmpty(),
|
||||
body("protectedKey").exists().isString().trim().notEmpty(),
|
||||
body("protectedKeyIV").exists().isString().trim().notEmpty(),
|
||||
body("protectedKeyTag").exists().isString().trim().notEmpty(),
|
||||
body("publicKey").exists().trim().notEmpty(),
|
||||
body("encryptedPrivateKey").exists().isString().trim().notEmpty(),
|
||||
body("encryptedPrivateKeyIV").exists().isString().trim().notEmpty(),
|
||||
body("encryptedPrivateKeyTag").exists().isString().trim().notEmpty(),
|
||||
body("salt").exists().isString().trim().notEmpty(),
|
||||
body("verifier").exists().isString().trim().notEmpty(),
|
||||
validateRequest,
|
||||
signupController.completeAccountInvite
|
||||
"/complete-account/invite", // TODO: consider moving to v3/users/new/complete-account/invite
|
||||
disableSignUpByServerCfg,
|
||||
authLimiter,
|
||||
requireSignupAuth,
|
||||
body("email").exists().isString().trim().notEmpty().isEmail(),
|
||||
body("firstName").exists().isString().trim().notEmpty(),
|
||||
body("lastName").exists().isString().trim().notEmpty(),
|
||||
body("protectedKey").exists().isString().trim().notEmpty(),
|
||||
body("protectedKeyIV").exists().isString().trim().notEmpty(),
|
||||
body("protectedKeyTag").exists().isString().trim().notEmpty(),
|
||||
body("publicKey").exists().trim().notEmpty(),
|
||||
body("encryptedPrivateKey").exists().isString().trim().notEmpty(),
|
||||
body("encryptedPrivateKeyIV").exists().isString().trim().notEmpty(),
|
||||
body("encryptedPrivateKeyTag").exists().isString().trim().notEmpty(),
|
||||
body("salt").exists().isString().trim().notEmpty(),
|
||||
body("verifier").exists().isString().trim().notEmpty(),
|
||||
validateRequest,
|
||||
signupController.completeAccountInvite
|
||||
);
|
||||
|
||||
export default router;
|
||||
export default router;
|
||||
|
||||
@@ -2,10 +2,11 @@ import express from "express";
|
||||
const router = express.Router();
|
||||
import { signupController } from "../../controllers/v3";
|
||||
import { authLimiter } from "../../helpers/rateLimiter";
|
||||
import { validateRequest } from "../../middleware";
|
||||
import { disableSignUpByServerCfg, validateRequest } from "../../middleware";
|
||||
|
||||
router.post(
|
||||
"/complete-account/signup", // TODO: consider moving endpoint to v3/users/new/complete-account/signup
|
||||
disableSignUpByServerCfg,
|
||||
authLimiter,
|
||||
validateRequest,
|
||||
signupController.completeAccountSignup
|
||||
|
||||
@@ -8,30 +8,28 @@ import {
|
||||
SMTP_HOST_ZOHOMAIL
|
||||
} from "../variables";
|
||||
import SMTPConnection from "nodemailer/lib/smtp-connection";
|
||||
import * as Sentry from "@sentry/node";
|
||||
import {
|
||||
getSmtpHost,
|
||||
getSmtpPassword,
|
||||
getSmtpPort,
|
||||
getSmtpSecure,
|
||||
getSmtpUsername,
|
||||
getSmtpUsername
|
||||
} from "../config";
|
||||
import { logger } from "../utils/logging";
|
||||
|
||||
export const initSmtp = async () => {
|
||||
const mailOpts: SMTPConnection.Options = {
|
||||
host: await getSmtpHost(),
|
||||
port: await getSmtpPort(),
|
||||
port: await getSmtpPort()
|
||||
};
|
||||
|
||||
if ((await getSmtpUsername()) && (await getSmtpPassword())) {
|
||||
mailOpts.auth = {
|
||||
user: await getSmtpUsername(),
|
||||
pass: await getSmtpPassword(),
|
||||
pass: await getSmtpPassword()
|
||||
};
|
||||
}
|
||||
|
||||
if ((await getSmtpSecure()) ? (await getSmtpSecure()) : false) {
|
||||
if ((await getSmtpSecure()) ? await getSmtpSecure() : false) {
|
||||
switch (await getSmtpHost()) {
|
||||
case SMTP_HOST_SENDGRID:
|
||||
mailOpts.requireTLS = true;
|
||||
@@ -39,38 +37,38 @@ export const initSmtp = async () => {
|
||||
case SMTP_HOST_MAILGUN:
|
||||
mailOpts.requireTLS = true;
|
||||
mailOpts.tls = {
|
||||
ciphers: "TLSv1.2",
|
||||
}
|
||||
ciphers: "TLSv1.2"
|
||||
};
|
||||
break;
|
||||
case SMTP_HOST_SOCKETLABS:
|
||||
mailOpts.requireTLS = true;
|
||||
mailOpts.tls = {
|
||||
ciphers: "TLSv1.2",
|
||||
}
|
||||
ciphers: "TLSv1.2"
|
||||
};
|
||||
break;
|
||||
case SMTP_HOST_ZOHOMAIL:
|
||||
mailOpts.requireTLS = true;
|
||||
mailOpts.tls = {
|
||||
ciphers: "TLSv1.2",
|
||||
}
|
||||
ciphers: "TLSv1.2"
|
||||
};
|
||||
break;
|
||||
case SMTP_HOST_GMAIL:
|
||||
mailOpts.requireTLS = true;
|
||||
mailOpts.tls = {
|
||||
ciphers: "TLSv1.2",
|
||||
}
|
||||
ciphers: "TLSv1.2"
|
||||
};
|
||||
break;
|
||||
case SMTP_HOST_OFFICE365:
|
||||
mailOpts.requireTLS = true;
|
||||
mailOpts.tls = {
|
||||
ciphers: "TLSv1.2"
|
||||
}
|
||||
};
|
||||
break;
|
||||
default:
|
||||
if ((await getSmtpHost()).includes("amazonaws.com")) {
|
||||
mailOpts.tls = {
|
||||
ciphers: "TLSv1.2",
|
||||
}
|
||||
ciphers: "TLSv1.2"
|
||||
};
|
||||
} else {
|
||||
mailOpts.secure = true;
|
||||
}
|
||||
@@ -79,20 +77,6 @@ export const initSmtp = async () => {
|
||||
}
|
||||
|
||||
const transporter = nodemailer.createTransport(mailOpts);
|
||||
transporter
|
||||
.verify()
|
||||
.then(async () => {
|
||||
Sentry.setUser(null);
|
||||
Sentry.captureMessage("SMTP - Successfully connected");
|
||||
logger.info("SMTP - Successfully connected");
|
||||
})
|
||||
.catch(async (err) => {
|
||||
Sentry.setUser(null);
|
||||
Sentry.captureException(
|
||||
`SMTP - Failed to connect to ${await getSmtpHost()}:${await getSmtpPort()} \n\t${err}`
|
||||
);
|
||||
logger.error(err, `SMTP - Failed to connect to ${await getSmtpHost()}:${await getSmtpPort()}`);
|
||||
});
|
||||
|
||||
return transporter;
|
||||
};
|
||||
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
initializeSamlStrategy
|
||||
} from "../authn/passport";
|
||||
import { logger } from "../logging";
|
||||
import { bootstrap } from "../../bootstrap";
|
||||
|
||||
/**
|
||||
* Prepare Infisical upon startup. This includes tasks like:
|
||||
@@ -55,14 +56,15 @@ export const setup = async () => {
|
||||
await TelemetryService.logTelemetryMessage();
|
||||
|
||||
// initializing SMTP configuration
|
||||
setTransporter(await initSmtp());
|
||||
const transporter = await initSmtp();
|
||||
setTransporter(transporter);
|
||||
|
||||
// initializing global feature set
|
||||
await EELicenseService.initGlobalFeatureSet();
|
||||
|
||||
// initializing auth strategies
|
||||
await initializeGoogleStrategy();
|
||||
await initializeGitHubStrategy()
|
||||
await initializeGitHubStrategy();
|
||||
await initializeGitLabStrategy();
|
||||
await initializeSamlStrategy();
|
||||
|
||||
@@ -73,6 +75,7 @@ export const setup = async () => {
|
||||
|
||||
// initializing the database connection
|
||||
await DatabaseService.initDatabase(await getMongoURL());
|
||||
await bootstrap({ transporter });
|
||||
|
||||
/**
|
||||
* NOTE: the order in this setup function is critical.
|
||||
@@ -92,7 +95,7 @@ export const setup = async () => {
|
||||
await backfillTrustedIps();
|
||||
await backfillUserAuthMethods();
|
||||
// await backfillPermission();
|
||||
await migrateRoleFromOwnerToAdmin()
|
||||
await migrateRoleFromOwnerToAdmin();
|
||||
|
||||
// re-encrypt any data previously encrypted under server hex 128-bit ENCRYPTION_KEY
|
||||
// to base64 256-bit ROOT_ENCRYPTION_KEY
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const UpdateServerConfigV1 = z.object({
|
||||
body: z.object({
|
||||
allowSignUp: z.boolean().optional()
|
||||
})
|
||||
});
|
||||
|
||||
export const SignupV1 = z.object({
|
||||
body: z.object({
|
||||
email: z.string().email().trim(),
|
||||
firstName: z.string().trim(),
|
||||
lastName: z.string().trim().optional(),
|
||||
protectedKey: z.string().trim(),
|
||||
protectedKeyIV: z.string().trim(),
|
||||
protectedKeyTag: z.string().trim(),
|
||||
publicKey: z.string().trim(),
|
||||
encryptedPrivateKey: z.string().trim(),
|
||||
encryptedPrivateKeyIV: z.string().trim(),
|
||||
encryptedPrivateKeyTag: z.string().trim(),
|
||||
salt: z.string().trim(),
|
||||
verifier: z.string().trim()
|
||||
})
|
||||
});
|
||||
Reference in New Issue
Block a user