Merge remote-tracking branch 'origin' into org-based-auth

This commit is contained in:
Tuan Dang
2024-02-07 09:49:13 -08:00
26 changed files with 570 additions and 366 deletions
+1 -1
View File
@@ -26,7 +26,7 @@ infisical:
pullPolicy: Always pullPolicy: Always
deploymentAnnotations: deploymentAnnotations:
secrets.infisical.com/auto-reload: "false" secrets.infisical.com/auto-reload: "true"
kubeSecretRef: "infisical-gamma-secrets" kubeSecretRef: "infisical-gamma-secrets"
-1
View File
@@ -104,7 +104,6 @@ ENV NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID \
WORKDIR / WORKDIR /
COPY --from=backend-runner /app /backend COPY --from=backend-runner /app /backend
COPY --from=backend-runner /app/dist/services/smtp/templates /backend/dist/templates
COPY --from=frontend-runner /app ./backend/frontend-build COPY --from=frontend-runner /app ./backend/frontend-build
+16 -14
View File
@@ -33,7 +33,7 @@
<img src="https://img.shields.io/github/commit-activity/m/infisical/infisical" alt="git commit activity" /> <img src="https://img.shields.io/github/commit-activity/m/infisical/infisical" alt="git commit activity" />
</a> </a>
<a href="https://cloudsmith.io/~infisical/repos/"> <a href="https://cloudsmith.io/~infisical/repos/">
<img src="https://img.shields.io/badge/Downloads-2.58M-orange" alt="Cloudsmith downloads" /> <img src="https://img.shields.io/badge/Downloads-6.95M-orange" alt="Cloudsmith downloads" />
</a> </a>
<a href="https://infisical.com/slack"> <a href="https://infisical.com/slack">
<img src="https://img.shields.io/badge/chat-on%20Slack-blueviolet" alt="Slack community channel" /> <img src="https://img.shields.io/badge/chat-on%20Slack-blueviolet" alt="Slack community channel" />
@@ -53,17 +53,19 @@ We're on a mission to make secret management more accessible to everyone, not ju
## Features ## Features
- **[User-friendly dashboard](https://infisical.com/docs/documentation/platform/project)** to manage secrets across projects and environments (e.g. development, production, etc.) - **[User-friendly dashboard](https://infisical.com/docs/documentation/platform/project)** to manage secrets across projects and environments (e.g. development, production, etc.).
- **[Client SDKs](https://infisical.com/docs/sdks/overview)** to fetch secrets for your apps and infrastructure on demand - **[Client SDKs](https://infisical.com/docs/sdks/overview)** to fetch secrets for your apps and infrastructure on demand.
- **[Infisical CLI](https://infisical.com/docs/cli/overview)** to fetch and inject secrets into any framework in local development - **[Infisical CLI](https://infisical.com/docs/cli/overview)** to fetch and inject secrets into any framework in local development and CI/CD.
- **[Native integrations](https://infisical.com/docs/integrations/overview)** with platforms like GitHub, Vercel, Netlify, and more - **[Infisical API](https://infisical.com/docs/api-reference/overview/introduction)** to perform CRUD operation on secrets, users, projects, and any other resource in Infisical.
- [**Automatic Kubernetes deployment secret reloads**](https://infisical.com/docs/documentation/getting-started/kubernetes) - **[Native integrations](https://infisical.com/docs/integrations/overview)** with platforms like [GitHub](https://infisical.com/docs/integrations/cicd/githubactions), [Vercel](https://infisical.com/docs/integrations/cloud/vercel), [AWS](https://infisical.com/docs/integrations/cloud/aws-secret-manager), and tools like [Terraform](https://infisical.com/docs/integrations/frameworks/terraform), [Ansible](https://infisical.com/docs/integrations/platforms/ansible), and more.
- **[Complete control over your data](https://infisical.com/docs/self-hosting/overview)** - host it yourself on any infrastructure - **[Infisical Kubernetes operator](https://infisical.com/docs/documentation/getting-started/kubernetes)** to managed secrets in k8s, automatically reload deployments, and more.
- **[Secret versioning](https://infisical.com/docs/documentation/platform/secret-versioning)** and **[Point-in-Time Recovery]()** to version every secret and project state - **[Infisical Agent](https://infisical.com/docs/infisical-agent/overview)** to inject secrets into your applications without modifying any code logic.
- **[Audit logs](https://infisical.com/docs/documentation/platform/audit-logs)** to record every action taken in a project - **[Self-hosting and on-prem](https://infisical.com/docs/self-hosting/overview)** to get complete control over your data.
- **Role-based Access Controls** per environment - **[Secret versioning](https://infisical.com/docs/documentation/platform/secret-versioning)** and **[Point-in-Time Recovery](https://infisical.com/docs/documentation/platform/pit-recovery)** to version every secret and project state.
- [**Simple on-premise deployments** to AWS, Digital Ocean, and more](https://infisical.com/docs/self-hosting/overview) - **[Audit logs](https://infisical.com/docs/documentation/platform/audit-logs)** to record every action taken in a project.
- [**Secret Scanning and Leak Prevention**](https://infisical.com/docs/cli/scanning-overview) - **[Role-based Access Controls](https://infisical.com/docs/documentation/platform/role-based-access-controls)** to create permission sets on any resource in Infisica and assign those to user or machine identities.
- **[Simple on-premise deployments](https://infisical.com/docs/self-hosting/overview)** to AWS, Digital Ocean, and more.
- **[Secret Scanning and Leak Prevention](https://infisical.com/docs/cli/scanning-overview)** to prevent secrets from leaking to git.
And much more. And much more.
@@ -115,9 +117,9 @@ Lean about Infisical's code scanning feature [here](https://infisical.com/docs/c
This repo available under the [MIT expat license](https://github.com/Infisical/infisical/blob/main/LICENSE), with the exception of the `ee` directory which will contain premium enterprise features requiring a Infisical license. This repo available under the [MIT expat license](https://github.com/Infisical/infisical/blob/main/LICENSE), with the exception of the `ee` directory which will contain premium enterprise features requiring a Infisical license.
If you are interested in managed Infisical Cloud of self-hosted Enterprise Offering, take a look at [our website](https://infisical.com/) or [book a meeting with us](https://cal.com/vmatsiiako/infisical-demo): If you are interested in managed Infisical Cloud of self-hosted Enterprise Offering, take a look at [our website](https://infisical.com/) or [book a meeting with us](https://infisical.cal.com/vlad/infisical-demo):
<a href="https://cal.com/vmatsiiako/infisical-demo"><img alt="Schedule a meeting" src="https://cal.com/book-with-cal-dark.svg" /></a> <a href="[https://infisical.cal.com/vlad/infisical-demo](https://infisical.cal.com/vlad/infisical-demo)"><img alt="Schedule a meeting" src="https://cal.com/book-with-cal-dark.svg" /></a>
## Security ## Security
+358 -254
View File
File diff suppressed because it is too large Load Diff
+9 -8
View File
@@ -2,12 +2,12 @@
"name": "backend", "name": "backend",
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "",
"main": "index.js", "main": "./dist/main.mjs",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"dev": "tsx watch --clear-screen=false ./src/main.ts | pino-pretty --colorize --colorizeObjects --singleLine", "dev": "tsx watch --clear-screen=false ./src/main.ts | pino-pretty --colorize --colorizeObjects --singleLine",
"dev:docker": "nodemon", "dev:docker": "nodemon",
"build": "rimraf dist && tsup && cp -R ./src/lib/validator/disposable_emails.txt ./dist && cp -R ./src/services/smtp/templates ./dist", "build": "tsup",
"start": "node dist/main.mjs", "start": "node dist/main.mjs",
"type:check": "tsc --noEmit", "type:check": "tsc --noEmit",
"lint:fix": "eslint --fix --ext js,ts ./src", "lint:fix": "eslint --fix --ext js,ts ./src",
@@ -44,7 +44,13 @@
"@types/pg": "^8.10.9", "@types/pg": "^8.10.9",
"@types/picomatch": "^2.3.3", "@types/picomatch": "^2.3.3",
"@types/prompt-sync": "^4.2.3", "@types/prompt-sync": "^4.2.3",
"@types/resolve": "^1.20.6",
"@types/uuid": "^9.0.7", "@types/uuid": "^9.0.7",
"@typescript-eslint/eslint-plugin": "^6.20.0",
"@typescript-eslint/parser": "^6.20.0",
"eslint": "^8.56.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-airbnb-typescript": "^17.1.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-import-resolver-typescript": "^3.6.1", "eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.29.1", "eslint-plugin-import": "^2.29.1",
@@ -55,6 +61,7 @@
"prompt-sync": "^4.2.0", "prompt-sync": "^4.2.0",
"rimraf": "^5.0.5", "rimraf": "^5.0.5",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"tsc-alias": "^1.8.8",
"tsconfig-paths": "^4.2.0", "tsconfig-paths": "^4.2.0",
"tsup": "^8.0.1", "tsup": "^8.0.1",
"tsx": "^4.4.0", "tsx": "^4.4.0",
@@ -80,8 +87,6 @@
"@octokit/webhooks-types": "^7.3.1", "@octokit/webhooks-types": "^7.3.1",
"@serdnam/pino-cloudwatch-transport": "^1.0.4", "@serdnam/pino-cloudwatch-transport": "^1.0.4",
"@sindresorhus/slugify": "^2.2.1", "@sindresorhus/slugify": "^2.2.1",
"@typescript-eslint/eslint-plugin": "^6.20.0",
"@typescript-eslint/parser": "^6.20.0",
"@ucast/mongo2js": "^1.3.4", "@ucast/mongo2js": "^1.3.4",
"ajv": "^8.12.0", "ajv": "^8.12.0",
"argon2": "^0.31.2", "argon2": "^0.31.2",
@@ -91,9 +96,6 @@
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"bullmq": "^5.1.1", "bullmq": "^5.1.1",
"dotenv": "^16.3.1", "dotenv": "^16.3.1",
"eslint": "^8.56.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-airbnb-typescript": "^17.1.0",
"fastify": "^4.24.3", "fastify": "^4.24.3",
"fastify-plugin": "^4.5.1", "fastify-plugin": "^4.5.1",
"handlebars": "^4.7.8", "handlebars": "^4.7.8",
@@ -108,7 +110,6 @@
"nanoid": "^5.0.4", "nanoid": "^5.0.4",
"node-cache": "^5.1.2", "node-cache": "^5.1.2",
"nodemailer": "^6.9.7", "nodemailer": "^6.9.7",
"ora": "^7.0.1",
"passport-github": "^1.1.0", "passport-github": "^1.1.0",
"passport-gitlab2": "^5.0.0", "passport-gitlab2": "^5.0.0",
"passport-google-oauth20": "^2.0.0", "passport-google-oauth20": "^2.0.0",
+10 -2
View File
@@ -1,10 +1,18 @@
import knex from "knex"; import knex from "knex";
export type TDbClient = ReturnType<typeof initDbConnection>; export type TDbClient = ReturnType<typeof initDbConnection>;
export const initDbConnection = (dbConnectionUri: string) => { export const initDbConnection = ({ dbConnectionUri, dbRootCert }: { dbConnectionUri: string; dbRootCert?: string }) => {
const db = knex({ const db = knex({
client: "pg", client: "pg",
connection: dbConnectionUri connection: {
connectionString: dbConnectionUri,
ssl: dbRootCert
? {
rejectUnauthorized: true,
ca: Buffer.from(dbRootCert, "base64").toString("ascii")
}
: false
}
}); });
return db; return db;
+1 -1
View File
@@ -91,7 +91,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
// eslint-disable-next-line // eslint-disable-next-line
async (req, profile, cb) => { async (req, profile, cb) => {
try { try {
const serverCfg = getServerCfg(); const serverCfg = await getServerCfg();
if (!profile) throw new BadRequestError({ message: "Missing profile" }); if (!profile) throw new BadRequestError({ message: "Missing profile" });
const { firstName } = profile; const { firstName } = profile;
const email = profile?.email ?? (profile?.emailAddress as string); // emailRippling is added because in Rippling the field `email` reserved const email = profile?.email ?? (profile?.emailAddress as string); // emailRippling is added because in Rippling the field `email` reserved
+3 -1
View File
@@ -15,9 +15,11 @@ const envSchema = z
PORT: z.coerce.number().default(4000), PORT: z.coerce.number().default(4000),
REDIS_URL: zpStr(z.string()), REDIS_URL: zpStr(z.string()),
HOST: zpStr(z.string().default("localhost")), HOST: zpStr(z.string().default("localhost")),
DB_CONNECTION_URI: zpStr(z.string().describe("Postgres database conntection string")), DB_CONNECTION_URI: zpStr(z.string().describe("Postgres database connection string")),
DB_ROOT_CERT: zpStr(z.string().describe("Postgres database base64-encoded CA cert").optional()),
NODE_ENV: z.enum(["development", "test", "production"]).default("production"), NODE_ENV: z.enum(["development", "test", "production"]).default("production"),
SALT_ROUNDS: z.coerce.number().default(10), SALT_ROUNDS: z.coerce.number().default(10),
INITIAL_ORGANIZATION_NAME: zpStr(z.string().optional()),
// TODO(akhilmhdh): will be changed to one // TODO(akhilmhdh): will be changed to one
ENCRYPTION_KEY: zpStr(z.string().optional()), ENCRYPTION_KEY: zpStr(z.string().optional()),
ROOT_ENCRYPTION_KEY: zpStr(z.string().optional()), ROOT_ENCRYPTION_KEY: zpStr(z.string().optional()),
+6 -2
View File
@@ -12,7 +12,11 @@ dotenv.config();
const run = async () => { const run = async () => {
const logger = await initLogger(); const logger = await initLogger();
const appCfg = initEnvConfig(logger); const appCfg = initEnvConfig(logger);
const db = initDbConnection(appCfg.DB_CONNECTION_URI); const db = initDbConnection({
dbConnectionUri: appCfg.DB_CONNECTION_URI,
dbRootCert: appCfg.DB_ROOT_CERT
});
const smtp = smtpServiceFactory(formatSmtpConfig()); const smtp = smtpServiceFactory(formatSmtpConfig());
const queue = queueServiceFactory(appCfg.REDIS_URL); const queue = queueServiceFactory(appCfg.REDIS_URL);
@@ -36,7 +40,7 @@ const run = async () => {
port: appCfg.PORT, port: appCfg.PORT,
host: appCfg.HOST, host: appCfg.HOST,
listenTextResolver: (address) => { listenTextResolver: (address) => {
bootstrap(); void bootstrap();
return address; return address;
} }
}); });
+3 -3
View File
@@ -14,11 +14,10 @@ import fasitfy from "fastify";
import { Knex } from "knex"; import { Knex } from "knex";
import { Logger } from "pino"; import { Logger } from "pino";
import { getConfig } from "@app/lib/config/env";
import { TQueueServiceFactory } from "@app/queue"; import { TQueueServiceFactory } from "@app/queue";
import { TSmtpService } from "@app/services/smtp/smtp-service"; import { TSmtpService } from "@app/services/smtp/smtp-service";
import { getConfig } from "@lib/config/env";
import { globalRateLimiterCfg } from "./config/rateLimiter"; import { globalRateLimiterCfg } from "./config/rateLimiter";
import { fastifyErrHandler } from "./plugins/error-handler"; import { fastifyErrHandler } from "./plugins/error-handler";
import { registerExternalNextjs } from "./plugins/external-nextjs"; import { registerExternalNextjs } from "./plugins/external-nextjs";
@@ -40,6 +39,7 @@ export const main = async ({ db, smtp, logger, queue }: TMain) => {
const server = fasitfy({ const server = fasitfy({
logger, logger,
trustProxy: true, trustProxy: true,
connectionTimeout: 30 * 1000,
ignoreTrailingSlash: true ignoreTrailingSlash: true
}).withTypeProvider<ZodTypeProvider>(); }).withTypeProvider<ZodTypeProvider>();
@@ -75,7 +75,7 @@ export const main = async ({ db, smtp, logger, queue }: TMain) => {
if (appCfg.isProductionMode) { if (appCfg.isProductionMode) {
await server.register(registerExternalNextjs, { await server.register(registerExternalNextjs, {
standaloneMode: appCfg.STANDALONE_MODE, standaloneMode: appCfg.STANDALONE_MODE,
dir: path.join(__dirname, "../"), dir: path.join(__dirname, "../../"),
port: appCfg.PORT port: appCfg.PORT
}); });
} }
+2 -2
View File
@@ -12,9 +12,9 @@ type BootstrapOpt = {
db: Knex; db: Knex;
}; };
const bootstrapCb = () => { const bootstrapCb = async () => {
const appCfg = getConfig(); const appCfg = getConfig();
const serverCfg = getServerCfg(); const serverCfg = await getServerCfg();
if (!serverCfg.initialized) { if (!serverCfg.initialized) {
console.info(`Welcome to Infisical console.info(`Welcome to Infisical
@@ -45,6 +45,9 @@ export const registerExternalNextjs = async (
server.route({ server.route({
method: ["GET", "PUT", "PATCH", "POST", "DELETE"], method: ["GET", "PUT", "PATCH", "POST", "DELETE"],
url: "/*", url: "/*",
schema: {
hide: true
},
handler: (req, res) => handler: (req, res) =>
nextApp nextApp
.getRequestHandler()(req.raw, res.raw) .getRequestHandler()(req.raw, res.raw)
+2 -1
View File
@@ -43,6 +43,7 @@ export const fastifySwagger = fp(async (fastify) => {
}); });
await fastify.register(swaggerUI, { await fastify.register(swaggerUI, {
routePrefix: "/docs" routePrefix: "/api/docs",
prefix: "/api/docs"
}); });
}); });
+2 -2
View File
@@ -513,9 +513,9 @@ export const registerRoutes = async (
}) })
} }
}, },
handler: () => { handler: async () => {
const cfg = getConfig(); const cfg = getConfig();
const serverCfg = getServerCfg(); const serverCfg = await getServerCfg();
return { return {
date: new Date(), date: new Date(),
message: "Ok" as const, message: "Ok" as const,
+3 -3
View File
@@ -20,8 +20,8 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
}) })
} }
}, },
handler: () => { handler: async () => {
const config = getServerCfg(); const config = await getServerCfg();
return { config }; return { config };
} }
}); });
@@ -78,7 +78,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
}, },
handler: async (req, res) => { handler: async (req, res) => {
const appCfg = getConfig(); const appCfg = getConfig();
const serverCfg = getServerCfg(); const serverCfg = await getServerCfg();
if (serverCfg.initialized) if (serverCfg.initialized)
throw new UnauthorizedError({ name: "Admin sign up", message: "Admin has been created" }); throw new UnauthorizedError({ name: "Admin sign up", message: "Admin has been created" });
const { user, token } = await server.services.superAdmin.adminSignUp({ const { user, token } = await server.services.superAdmin.adminSignUp({
+3 -3
View File
@@ -42,7 +42,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
async (req, _accessToken, _refreshToken, profile, cb) => { async (req, _accessToken, _refreshToken, profile, cb) => {
try { try {
const email = profile?.emails?.[0]?.value; const email = profile?.emails?.[0]?.value;
const serverCfg = getServerCfg(); const serverCfg = await getServerCfg();
if (!email) if (!email)
throw new BadRequestError({ throw new BadRequestError({
message: "Email not found", message: "Email not found",
@@ -84,7 +84,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
try { try {
const ghEmails = await fetchGithubEmails(accessToken); const ghEmails = await fetchGithubEmails(accessToken);
const { email } = ghEmails.filter((gitHubEmail) => gitHubEmail.primary)[0]; const { email } = ghEmails.filter((gitHubEmail) => gitHubEmail.primary)[0];
const serverCfg = getServerCfg(); const serverCfg = await getServerCfg();
const { isUserCompleted, providerAuthToken } = await server.services.login.oauth2Login({ const { isUserCompleted, providerAuthToken } = await server.services.login.oauth2Login({
email, email,
firstName: profile.displayName, firstName: profile.displayName,
@@ -120,7 +120,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
async (req: any, _accessToken: string, _refreshToken: string, profile: any, cb: any) => { async (req: any, _accessToken: string, _refreshToken: string, profile: any, cb: any) => {
try { try {
const email = profile.emails[0].value; const email = profile.emails[0].value;
const serverCfg = getServerCfg(); const serverCfg = await getServerCfg();
const { isUserCompleted, providerAuthToken } = await server.services.login.oauth2Login({ const { isUserCompleted, providerAuthToken } = await server.services.login.oauth2Login({
email, email,
firstName: profile.displayName, firstName: profile.displayName,
@@ -478,7 +478,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
// TODO: Move to telemetry plugin // TODO: Move to telemetry plugin
let shouldRecordK8Event = false; let shouldRecordK8Event = false;
if (req.headers["user-agent"] === "k8-operator") { if (req.headers["user-agent"] === "k8-operatoer") {
const randomNumber = Math.random(); const randomNumber = Math.random();
if (randomNumber > 0.95) { if (randomNumber > 0.95) {
shouldRecordK8Event = true; shouldRecordK8Event = true;
+10 -2
View File
@@ -21,7 +21,11 @@ export const projectDALFactory = (db: TDbClient) => {
db.ref("slug").withSchema(TableName.Environment).as("envSlug"), db.ref("slug").withSchema(TableName.Environment).as("envSlug"),
db.ref("name").withSchema(TableName.Environment).as("envName") db.ref("name").withSchema(TableName.Environment).as("envName")
) )
.orderBy("createdAt", "asc", "last"); .orderBy([
{ column: `${TableName.Project}.name`, order: "asc" },
{ column: `${TableName.Environment}.position`, order: "asc" }
]);
const nestedWorkspaces = sqlNestRelationships({ const nestedWorkspaces = sqlNestRelationships({
data: workspaces, data: workspaces,
key: "id", key: "id",
@@ -102,7 +106,11 @@ export const projectDALFactory = (db: TDbClient) => {
db.ref("id").withSchema(TableName.Environment).as("envId"), db.ref("id").withSchema(TableName.Environment).as("envId"),
db.ref("slug").withSchema(TableName.Environment).as("envSlug"), db.ref("slug").withSchema(TableName.Environment).as("envSlug"),
db.ref("name").withSchema(TableName.Environment).as("envName") db.ref("name").withSchema(TableName.Environment).as("envName")
); )
.orderBy([
{ column: `${TableName.Project}.name`, order: "asc" },
{ column: `${TableName.Environment}.position`, order: "asc" }
]);
return sqlNestRelationships({ return sqlNestRelationships({
data: workspaces, data: workspaces,
key: "id", key: "id",
+17 -5
View File
@@ -569,18 +569,30 @@ export const secretServiceFactory = ({
const secretBlindIndex = await interalGenSecBlindIndexByName(projectId, secretName); const secretBlindIndex = await interalGenSecBlindIndexByName(projectId, secretName);
const secret = await (typeof version !== undefined // Case: The old python SDK uses incorrect logic https://github.com/Infisical/infisical-python/blob/main/infisical/client/infisicalclient.py#L89.
// Fetch secrets using service tokens cannot fetch personal secrets, only shared.
// The mongo backend used to correct this mistake, this line also adds it to current backend
// Mongo backend check: https://github.com/Infisical/infisical-mongo/blob/main/backend/src/helpers/secrets.ts#L658
let secretType = type;
if (actor === ActorType.SERVICE) {
logger.info(
`secretServiceFactory: overriding secret type for service token [projectId=${projectId}] [factoryFunctionName=getSecretByName]`
);
secretType = SecretType.Shared;
}
const secret = await (typeof version === undefined
? secretDAL.findOne({ ? secretDAL.findOne({
folderId, folderId,
type, type: secretType,
userId: type === SecretType.Personal ? actorId : null, userId: secretType === SecretType.Personal ? actorId : null,
secretBlindIndex secretBlindIndex
}) })
: secretVersionDAL : secretVersionDAL
.findOne({ .findOne({
folderId, folderId,
type, type: secretType,
userId: type === SecretType.Personal ? actorId : null, userId: secretType === SecretType.Personal ? actorId : null,
secretBlindIndex secretBlindIndex
}) })
.then((el) => SecretsSchema.parse({ ...el, id: el.secretId }))); .then((el) => SecretsSchema.parse({ ...el, id: el.secretId })));
@@ -1,4 +1,5 @@
import { TSuperAdmin, TSuperAdminUpdate } from "@app/db/schemas"; import { TSuperAdmin, TSuperAdminUpdate } from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { TAuthLoginFactory } from "../auth/auth-login-service"; import { TAuthLoginFactory } from "../auth/auth-login-service";
@@ -17,11 +18,8 @@ type TSuperAdminServiceFactoryDep = {
export type TSuperAdminServiceFactory = ReturnType<typeof superAdminServiceFactory>; export type TSuperAdminServiceFactory = ReturnType<typeof superAdminServiceFactory>;
let serverCfg: Readonly<TSuperAdmin>; // eslint-disable-next-line
export const getServerCfg = () => { export let getServerCfg: () => Promise<TSuperAdmin>;
if (!serverCfg) throw new BadRequestError({ name: "Get server cfg", message: "Server cfg not initialized" });
return serverCfg;
};
export const superAdminServiceFactory = ({ export const superAdminServiceFactory = ({
serverCfgDAL, serverCfgDAL,
@@ -30,19 +28,18 @@ export const superAdminServiceFactory = ({
orgService orgService
}: TSuperAdminServiceFactoryDep) => { }: TSuperAdminServiceFactoryDep) => {
const initServerCfg = async () => { const initServerCfg = async () => {
serverCfg = await serverCfgDAL.findOne({}); // TODO(akhilmhdh): bad pattern time less change this later to me itself
if (!serverCfg) { getServerCfg = () => serverCfgDAL.findOne({});
const newCfg = await serverCfgDAL.create({ initialized: false, allowSignUp: true });
serverCfg = newCfg; const serverCfg = await serverCfgDAL.findOne({});
return newCfg; if (serverCfg) return;
} const newCfg = await serverCfgDAL.create({ initialized: false, allowSignUp: true });
return serverCfg; return newCfg;
}; };
const updateServerCfg = async (data: TSuperAdminUpdate) => { const updateServerCfg = async (data: TSuperAdminUpdate) => {
const serverCfg = await getServerCfg();
const cfg = await serverCfgDAL.updateById(serverCfg.id, data); const cfg = await serverCfgDAL.updateById(serverCfg.id, data);
serverCfg = cfg;
Object.freeze(serverCfg);
return cfg; return cfg;
}; };
@@ -62,6 +59,7 @@ export const superAdminServiceFactory = ({
ip, ip,
userAgent userAgent
}: TAdminSignUpDTO) => { }: TAdminSignUpDTO) => {
const appCfg = getConfig();
const existingUser = await userDAL.findOne({ email }); const existingUser = await userDAL.findOne({ email });
if (existingUser) throw new BadRequestError({ name: "Admin sign up", message: "User already exist" }); if (existingUser) throw new BadRequestError({ name: "Admin sign up", message: "User already exist" });
@@ -95,7 +93,10 @@ export const superAdminServiceFactory = ({
); );
return { user: newUser, enc: userEnc }; return { user: newUser, enc: userEnc };
}); });
await orgService.createOrganization(userInfo.user.id, userInfo.user.email, "Admin Org");
const initialOrganizationName = appCfg.INITIAL_ORGANIZATION_NAME ?? "Admin Org";
await orgService.createOrganization(userInfo.user.id, userInfo.user.email, initialOrganizationName);
await updateServerCfg({ initialized: true }); await updateServerCfg({ initialized: true });
const token = await authService.generateUserTokens({ const token = await authService.generateUserTokens({
+2 -4
View File
@@ -22,11 +22,9 @@
"skipLibCheck": true, "skipLibCheck": true,
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@app/*": ["./src/*"], "@app/*": ["./src/*"]
"@lib/*": ["./src/lib/*"],
"@server/*": ["./src/server/*"]
} }
}, },
"include": ["src/**/*", "scripts/**/*", "e2e-test/**/*","./.eslintrc.js"], "include": ["src/**/*", "scripts/**/*", "e2e-test/**/*", "./.eslintrc.js", "./tsup.config.js"],
"exclude": ["node_modules"] "exclude": ["node_modules"]
} }
+61 -2
View File
@@ -1,14 +1,73 @@
/* eslint-disable */
import path from "node:path";
import fs from "fs/promises";
import { replaceTscAliasPaths } from "tsc-alias";
import { defineConfig } from "tsup"; import { defineConfig } from "tsup";
// Instead of using tsx or tsc for building, consider using tsup.
// TSX serves as an alternative to Node.js, allowing you to build directly on the Node.js runtime.
// Its functionality mirrors Node.js, with the only difference being the absence of a final build step. Production should ideally be launched with TSX.
// TSC is effective for creating a final build, but it requires manual copying of all static files such as handlebars, emails, etc.
// A significant challenge is the shift towards ESM, as more packages are adopting ESM. If the output is in CommonJS, it may lead to errors.
// The suggested configuration offers a balance, accommodating both ESM and CommonJS requirements.
export default defineConfig({ export default defineConfig({
shims: true, shims: true,
clean: true,
minify: false,
keepNames: true,
splitting: false,
format: "esm", format: "esm",
// copy the files to output
loader: { loader: {
".handlebars": "copy", ".handlebars": "copy",
".md": "copy" ".md": "copy",
".txt": "copy"
}, },
external: ["../../../frontend/node_modules/next/dist/server/next-server.js"], external: ["../../../frontend/node_modules/next/dist/server/next-server.js"],
outDir: "dist", outDir: "dist",
tsconfig: "./tsconfig.json",
entry: ["./src"], entry: ["./src"],
sourceMap: "inline" sourceMap: true,
skipNodeModulesBundle: true,
esbuildPlugins: [
{
// esm directory import are not allowed
// /folder1 should be explicitly imported as /folder1/index.ts
// this plugin will append it automatically on build time to all imports
name: "commonjs-esm-directory-import",
setup(build) {
build.onResolve({ filter: /.*/ }, async (args) => {
if (args.importer) {
if (args.kind === "import-statement") {
const isRelativePath = args.path.startsWith(".");
const absPath = isRelativePath
? path.join(args.resolveDir, args.path)
: path.join(args.path.replace("@app", "./src"));
const isFile = await fs
.stat(`${absPath}.ts`)
.then((el) => el.isFile)
.catch((err) => err.code === "ENOTDIR");
return {
path: isFile ? `${args.path}.mjs` : `${args.path}/index.mjs`,
external: true
};
}
}
return undefined;
});
}
}
],
async onSuccess() {
// this will replace all tsconfig paths
await replaceTscAliasPaths({
configFile: "tsconfig.json",
watch: false,
outDir: "dist"
});
}
}); });
@@ -1,6 +1,6 @@
--- ---
title: "Role-based Access Controls" title: "Role-based Access Controls"
description: "Infisical's Role-based Acccess Controls enable creating permissions for user and machine identities to restrict access to resources and the range of actions that can performed." description: "Infisical's Role-based Access Controls enable creating permissions for user and machine identities to restrict access to resources and the range of actions that can be performed."
--- ---
### General access controls ### General access controls
+12 -10
View File
@@ -617,16 +617,18 @@ export const AppLayout = ({ children }: LayoutProps) => {
</MenuItem> </MenuItem>
</a> </a>
</Link> </Link>
<Link href={`/org/${currentOrg?.id}/billing`} passHref> {(window.location.origin.includes("https://app.infisical.com")) && (
<a> <Link href={`/org/${currentOrg?.id}/billing`} passHref>
<MenuItem <a>
isSelected={router.asPath === `/org/${currentOrg?.id}/billing`} <MenuItem
icon="system-outline-103-coin-cash-monetization" isSelected={router.asPath === `/org/${currentOrg?.id}/billing`}
> icon="system-outline-103-coin-cash-monetization"
Usage & Billing >
</MenuItem> Usage & Billing
</a> </MenuItem>
</Link> </a>
</Link>
)}
<Link href={`/org/${currentOrg?.id}/settings`} passHref> <Link href={`/org/${currentOrg?.id}/settings`} passHref>
<a> <a>
<MenuItem <MenuItem
@@ -30,35 +30,35 @@ export const AdminDashboardPage = () => {
return ( return (
<div className="container mx-auto max-w-7xl pb-12 text-white dark:[color-scheme:dark]"> <div className="container mx-auto max-w-7xl pb-12 text-white dark:[color-scheme:dark]">
<div className="mb-8"> <div className="mx-auto mb-6 w-full max-w-7xl py-6 px-6">
<div className="mb-4 mt-6 flex flex-col items-start justify-between text-xl"> <div className="mb-8 flex flex-col items-start justify-between text-xl">
<h1 className="text-3xl font-semibold">Admin Dashboard</h1> <h1 className="text-3xl font-semibold">Admin Dashboard</h1>
<p className="text-base text-bunker-300">Manage your Infisical</p> <p className="text-base text-bunker-300">Manage your Infisical instance.</p>
</div> </div>
{isUserLoading || isNotAllowed ? (
<ContentLoader text={isNotAllowed ? "Redirecting to org page..." : undefined} />
) : (
<div>
<Tabs defaultValue={TabSections.Settings}>
<TabList>
<div className="flex w-full flex-row border-b border-mineshaft-600">
<Tab value={TabSections.Settings}>General</Tab>
</div>
</TabList>
<TabPanel value={TabSections.Settings}>
<div className="flex items-center space-x-4">
<Switch
id="disable-invite"
isChecked={Boolean(config?.allowSignUp)}
onCheckedChange={(isChecked) => updateServerConfig({ allowSignUp: isChecked })}
/>
<div className="flex-grow">Enable signup or invite</div>
</div>
</TabPanel>
</Tabs>
</div>
)}
</div> </div>
{isUserLoading || isNotAllowed ? (
<ContentLoader text={isNotAllowed ? "Redirecting to org page..." : undefined} />
) : (
<div>
<Tabs defaultValue={TabSections.Settings}>
<TabList>
<div className="flex w-full flex-row border-b border-mineshaft-600">
<Tab value={TabSections.Settings}>General</Tab>
</div>
</TabList>
<TabPanel value={TabSections.Settings}>
<div className="flex items-center space-x-4">
<Switch
id="disable-invite"
isChecked={Boolean(config?.allowSignUp)}
onCheckedChange={(isChecked) => updateServerConfig({ allowSignUp: isChecked })}
/>
<div className="flex-grow">Enable signup or invite</div>
</div>
</TabPanel>
</Tabs>
</div>
)}
</div> </div>
); );
}; };
@@ -61,7 +61,7 @@ export const SignUpPage = () => {
router.push("/login"); router.push("/login");
} }
} }
}, [config?.initialized]); }, []);
const { mutateAsync: createAdminUser } = useCreateAdminUser(); const { mutateAsync: createAdminUser } = useCreateAdminUser();