Merge remote-tracking branch 'origin/main' into feat-github-integration

This commit is contained in:
Salman
2024-02-15 00:00:05 +05:30
70 changed files with 684 additions and 5544 deletions
+7 -5
View File
@@ -3,16 +3,18 @@
# THIS IS A SAMPLE ENCRYPTION KEY AND SHOULD NEVER BE USED FOR PRODUCTION # THIS IS A SAMPLE ENCRYPTION KEY AND SHOULD NEVER BE USED FOR PRODUCTION
ENCRYPTION_KEY=6c1fe4e407b8911c104518103505b218 ENCRYPTION_KEY=6c1fe4e407b8911c104518103505b218
# Required
DB_CONNECTION_URI=postgres://infisical:infisical@db:5432/infisical
# JWT # JWT
# Required secrets to sign JWT tokens # Required secrets to sign JWT tokens
# THIS IS A SAMPLE AUTH_SECRET KEY AND SHOULD NEVER BE USED FOR PRODUCTION # THIS IS A SAMPLE AUTH_SECRET KEY AND SHOULD NEVER BE USED FOR PRODUCTION
AUTH_SECRET=5lrMXKKWCVocS/uerPsl7V+TX/aaUaI7iDkgl3tSmLE= AUTH_SECRET=5lrMXKKWCVocS/uerPsl7V+TX/aaUaI7iDkgl3tSmLE=
# MongoDB # Postgres creds
# Backend will connect to the MongoDB instance at connection string MONGO_URL which can either be a ref POSTGRES_PASSWORD=infisical
# to the MongoDB container instance or Mongo Cloud POSTGRES_USER=infisical
# Required POSTGRES_DB=infisical
MONGO_URL=mongodb://root:example@mongo:27017/?authSource=admin
# Redis # Redis
REDIS_URL=redis://redis:6379 REDIS_URL=redis://redis:6379
+1
View File
@@ -0,0 +1 @@
DB_CONNECTION_URI=
@@ -42,8 +42,28 @@ jobs:
- uses: actions/setup-go@v5 - uses: actions/setup-go@v5
with: with:
go-version: '1.21.5' go-version: '1.21.5'
- name: Wait for containers to be stable - name: Wait for container to be stable and check logs
run: timeout 60s sh -c 'until docker ps | grep infisical-api | grep -q healthy; do echo "Waiting for container to be healthy..."; sleep 2; done' run: |
SECONDS=0
HEALTHY=0
while [ $SECONDS -lt 60 ]; do
if docker ps | grep infisical-api | grep -q healthy; then
echo "Container is healthy."
HEALTHY=1
break
fi
echo "Waiting for container to be healthy... ($SECONDS seconds elapsed)"
docker logs infisical-api
sleep 2
SECONDS=$((SECONDS+2))
done
if [ $HEALTHY -ne 1 ]; then
echo "Container did not become healthy in time"
exit 1
fi
- name: Install openapi-diff - name: Install openapi-diff
run: go install github.com/tufin/oasdiff@latest run: go install github.com/tufin/oasdiff@latest
- name: Running OpenAPI Spec diff action - name: Running OpenAPI Spec diff action
+1 -1
View File
@@ -6,7 +6,7 @@ node_modules
.env.gamma .env.gamma
.env.prod .env.prod
.env.infisical .env.infisical
.env.migration
*~ *~
*.swp *.swp
*.swo *.swo
+2 -8
View File
@@ -5,16 +5,10 @@ push:
docker-compose -f docker-compose.yml push docker-compose -f docker-compose.yml push
up-dev: up-dev:
docker-compose -f docker-compose.dev.yml up --build docker compose -f docker-compose.dev.yml up --build
up-pg-dev:
docker compose -f docker-compose.pg.yml up --build
i-dev:
infisical run -- docker-compose -f docker-compose.dev.yml up --build
up-prod: up-prod:
docker-compose -f docker-compose.yml up --build docker-compose -f docker-compose.prod.yml up --build
down: down:
docker-compose down docker-compose down
+2 -2
View File
@@ -84,13 +84,13 @@ To set up and run Infisical locally, make sure you have Git and Docker installed
Linux/macOS: Linux/macOS:
```console ```console
git clone https://github.com/Infisical/infisical && cd "$(basename $_ .git)" && cp .env.example .env && docker-compose -f docker-compose.yml up git clone https://github.com/Infisical/infisical && cd "$(basename $_ .git)" && cp .env.example .env && docker-compose -f docker-compose.prod.yml up
``` ```
Windows Command Prompt: Windows Command Prompt:
```console ```console
git clone https://github.com/Infisical/infisical && cd infisical && copy .env.example .env && docker-compose -f docker-compose.yml up git clone https://github.com/Infisical/infisical && cd infisical && copy .env.example .env && docker-compose -f docker-compose.prod.yml up
``` ```
Create an account at `http://localhost:80` Create an account at `http://localhost:80`
+6 -29
View File
@@ -3,13 +3,9 @@ import dotenv from "dotenv";
import path from "path"; import path from "path";
import knex from "knex"; import knex from "knex";
import { writeFileSync } from "fs"; import { writeFileSync } from "fs";
import promptSync from "prompt-sync";
const prompt = promptSync({ sigint: true });
dotenv.config({ dotenv.config({
path: path.join(__dirname, "../.env"), path: path.join(__dirname, "../../.env.migration")
debug: true
}); });
const db = knex({ const db = knex({
@@ -94,17 +90,7 @@ const main = async () => {
.orderBy("table_name") .orderBy("table_name")
).filter((el) => !el.tableName.includes("_migrations")); ).filter((el) => !el.tableName.includes("_migrations"));
console.log("Select a table to generate schema");
console.table(tables);
console.log("all: all tables");
const selectedTables = prompt("Type table numbers comma seperated: ");
const tableNumbers =
selectedTables !== "all" ? selectedTables.split(",").map((el) => Number(el)) : [];
for (let i = 0; i < tables.length; i += 1) { for (let i = 0; i < tables.length; i += 1) {
// skip if not desired table
if (selectedTables !== "all" && !tableNumbers.includes(i)) continue;
const { tableName } = tables[i]; const { tableName } = tables[i];
const columns = await db(tableName).columnInfo(); const columns = await db(tableName).columnInfo();
const columnNames = Object.keys(columns); const columnNames = Object.keys(columns);
@@ -124,16 +110,16 @@ const main = async () => {
if (colInfo.nullable) { if (colInfo.nullable) {
ztype = ztype.concat(".nullable().optional()"); ztype = ztype.concat(".nullable().optional()");
} }
schema = schema.concat(`${!schema ? "\n" : ""} ${columnName}: ${ztype},\n`); schema = schema.concat(
`${!schema ? "\n" : ""} ${columnName}: ${ztype}${colNum === columnNames.length - 1 ? "" : ","}\n`
);
} }
const dashcase = tableName.split("_").join("-"); const dashcase = tableName.split("_").join("-");
const pascalCase = tableName const pascalCase = tableName
.split("_") .split("_")
.reduce( .reduce((prev, curr) => prev + `${curr.at(0)?.toUpperCase()}${curr.slice(1).toLowerCase()}`, "");
(prev, curr) => prev + `${curr.at(0)?.toUpperCase()}${curr.slice(1).toLowerCase()}`,
""
);
writeFileSync( writeFileSync(
path.join(__dirname, "../src/db/schemas", `${dashcase}.ts`), path.join(__dirname, "../src/db/schemas", `${dashcase}.ts`),
`// Code generated by automation script, DO NOT EDIT. `// Code generated by automation script, DO NOT EDIT.
@@ -152,15 +138,6 @@ export type T${pascalCase}Insert = Omit<T${pascalCase}, TImmutableDBKeys>;
export type T${pascalCase}Update = Partial<Omit<T${pascalCase}, TImmutableDBKeys>>; export type T${pascalCase}Update = Partial<Omit<T${pascalCase}, TImmutableDBKeys>>;
` `
); );
// const file = readFileSync(path.join(__dirname, "../src/db/schemas/index.ts"), "utf8");
// if (!file.includes(`export * from "./${dashcase};"`)) {
// appendFileSync(
// path.join(__dirname, "../src/db/schemas/index.ts"),
// `\nexport * from "./${dashcase}";`,
// "utf8"
// );
// }
} }
process.exit(0); process.exit(0);
+2 -2
View File
@@ -5,9 +5,9 @@ import dotenv from "dotenv";
import type { Knex } from "knex"; import type { Knex } from "knex";
import path from "path"; import path from "path";
// Update with your config settings. // Update with your config settings. .
dotenv.config({ dotenv.config({
path: path.join(__dirname, "../../.env"), path: path.join(__dirname, "../../../.env.migration"),
debug: true debug: true
}); });
export default { export default {
@@ -11,6 +11,13 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
method: "GET", method: "GET",
url: "/:workspaceId/secret-snapshots", url: "/:workspaceId/secret-snapshots",
schema: { schema: {
description: "Return project secret snapshots ids",
security: [
{
apiKeyAuth: [],
bearerAuth: []
}
],
params: z.object({ params: z.object({
workspaceId: z.string().trim() workspaceId: z.string().trim()
}), }),
@@ -74,6 +81,13 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
method: "GET", method: "GET",
url: "/:workspaceId/audit-logs", url: "/:workspaceId/audit-logs",
schema: { schema: {
description: "Return audit logs",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
workspaceId: z.string().trim() workspaceId: z.string().trim()
}), }),
+2 -3
View File
@@ -19,7 +19,6 @@ import { BadRequestError } from "@app/lib/errors";
import { logger } from "@app/lib/logger"; import { logger } from "@app/lib/logger";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
type TSAMLConfig = { type TSAMLConfig = {
callbackUrl: string; callbackUrl: string;
@@ -28,6 +27,7 @@ type TSAMLConfig = {
cert: string; cert: string;
audience: string; audience: string;
wantAuthnResponseSigned?: boolean; wantAuthnResponseSigned?: boolean;
disableRequestedAuthnContext?: boolean;
}; };
export const registerSamlRouter = async (server: FastifyZodProvider) => { export const registerSamlRouter = async (server: FastifyZodProvider) => {
@@ -77,6 +77,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
samlConfig.wantAuthnResponseSigned = false; samlConfig.wantAuthnResponseSigned = false;
} }
if (ssoConfig.authProvider === SamlProviders.AZURE_SAML) { if (ssoConfig.authProvider === SamlProviders.AZURE_SAML) {
samlConfig.disableRequestedAuthnContext = true;
if (req.body?.RelayState && JSON.parse(req.body.RelayState).spInitiated) { if (req.body?.RelayState && JSON.parse(req.body.RelayState).spInitiated) {
samlConfig.audience = `spn:${ssoConfig.issuer}`; samlConfig.audience = `spn:${ssoConfig.issuer}`;
} }
@@ -92,7 +93,6 @@ 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 = 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
@@ -105,7 +105,6 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
email, email,
firstName: profile.firstName as string, firstName: profile.firstName as string,
lastName: profile.lastName as string, lastName: profile.lastName as string,
isSignupAllowed: Boolean(serverCfg.allowSignUp),
relayState: (req.body as { RelayState?: string }).RelayState, relayState: (req.body as { RelayState?: string }).RelayState,
authProvider: (req as unknown as FastifyRequest).ssoConfig?.authProvider as string, authProvider: (req as unknown as FastifyRequest).ssoConfig?.authProvider as string,
orgId: (req as unknown as FastifyRequest).ssoConfig?.orgId as string orgId: (req as unknown as FastifyRequest).ssoConfig?.orgId as string
@@ -57,6 +57,13 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
method: "POST", method: "POST",
url: "/:secretSnapshotId/rollback", url: "/:secretSnapshotId/rollback",
schema: { schema: {
description: "Roll back project secrets to those captured in a secret snapshot version.",
security: [
{
apiKeyAuth: [],
bearerAuth: []
}
],
params: z.object({ params: z.object({
secretSnapshotId: z.string().trim() secretSnapshotId: z.string().trim()
}), }),
@@ -300,19 +300,9 @@ export const samlConfigServiceFactory = ({
}; };
}; };
const samlLogin = async ({ const samlLogin = async ({ firstName, email, lastName, authProvider, orgId, relayState }: TSamlLoginDTO) => {
firstName,
email,
lastName,
authProvider,
orgId,
relayState,
isSignupAllowed
}: TSamlLoginDTO) => {
const appCfg = getConfig(); const appCfg = getConfig();
let user = await userDAL.findUserByEmail(email); let user = await userDAL.findUserByEmail(email);
const isSamlSignUpDisabled = !isSignupAllowed && !user;
if (isSamlSignUpDisabled) throw new BadRequestError({ message: "User signup disabled", name: "Saml SSO login" });
const organization = await orgDAL.findOrgById(orgId); const organization = await orgDAL.findOrgById(orgId);
if (!organization) throw new BadRequestError({ message: "Org not found" }); if (!organization) throw new BadRequestError({ message: "Org not found" });
@@ -41,7 +41,6 @@ export type TSamlLoginDTO = {
lastName?: string; lastName?: string;
authProvider: string; authProvider: string;
orgId: string; orgId: string;
isSignupAllowed: boolean;
// saml thingy // saml thingy
relayState?: string; relayState?: string;
}; };
+3 -3
View File
@@ -25,13 +25,13 @@ export const fastifySwagger = fp(async (fastify) => {
], ],
components: { components: {
securitySchemes: { securitySchemes: {
bearer: { bearerAuth: {
type: "http", type: "http",
scheme: "bearer", scheme: "bearer",
bearerFormat: "JWT", bearerFormat: "JWT",
description: "A service token in Infisical" description: "An access token in Infisical"
}, },
apiKey: { apiKeyAuth: {
type: "apiKey", type: "apiKey",
in: "header", in: "header",
name: "X-API-Key", name: "X-API-Key",
+4 -2
View File
@@ -72,7 +72,8 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
200: z.object({ 200: z.object({
message: z.string(), message: z.string(),
user: UsersSchema, user: UsersSchema,
token: z.string() token: z.string(),
new: z.string()
}) })
} }
}, },
@@ -107,7 +108,8 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
return { return {
message: "Successfully set up admin account", message: "Successfully set up admin account",
user: user.user, user: user.user,
token: token.access token: token.access,
new: "123"
}; };
} }
}); });
@@ -5,6 +5,7 @@ export const registerIdentityAccessTokenRouter = async (server: FastifyZodProvid
url: "/token/renew", url: "/token/renew",
method: "POST", method: "POST",
schema: { schema: {
description: "Renew access token",
body: z.object({ body: z.object({
accessToken: z.string().trim() accessToken: z.string().trim()
}), }),
@@ -11,6 +11,12 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
url: "/", url: "/",
onRequest: verifyAuth([AuthMode.JWT]), onRequest: verifyAuth([AuthMode.JWT]),
schema: { schema: {
description: "Create identity",
security: [
{
bearerAuth: []
}
],
body: z.object({ body: z.object({
name: z.string().trim(), name: z.string().trim(),
organizationId: z.string().trim(), organizationId: z.string().trim(),
@@ -52,6 +58,12 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
url: "/:identityId", url: "/:identityId",
onRequest: verifyAuth([AuthMode.JWT]), onRequest: verifyAuth([AuthMode.JWT]),
schema: { schema: {
description: "Update identity",
security: [
{
bearerAuth: []
}
],
params: z.object({ params: z.object({
identityId: z.string() identityId: z.string()
}), }),
@@ -95,6 +107,12 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
url: "/:identityId", url: "/:identityId",
onRequest: verifyAuth([AuthMode.JWT]), onRequest: verifyAuth([AuthMode.JWT]),
schema: { schema: {
description: "Delete identity",
security: [
{
bearerAuth: []
}
],
params: z.object({ params: z.object({
identityId: z.string() identityId: z.string()
}), }),
@@ -24,6 +24,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
url: "/universal-auth/login", url: "/universal-auth/login",
method: "POST", method: "POST",
schema: { schema: {
description: "Login with Universal Auth",
body: z.object({ body: z.object({
clientId: z.string().trim(), clientId: z.string().trim(),
clientSecret: z.string().trim() clientSecret: z.string().trim()
@@ -67,6 +68,12 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
method: "POST", method: "POST",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "Attach Universal Auth configuration onto identity",
security: [
{
bearerAuth: []
}
],
params: z.object({ params: z.object({
identityId: z.string().trim() identityId: z.string().trim()
}), }),
@@ -141,6 +148,12 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
method: "PATCH", method: "PATCH",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "Update Universal Auth configuration on identity",
security: [
{
bearerAuth: []
}
],
params: z.object({ params: z.object({
identityId: z.string() identityId: z.string()
}), }),
@@ -209,6 +222,12 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
method: "GET", method: "GET",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "Retrieve Universal Auth configuration on identity",
security: [
{
bearerAuth: []
}
],
params: z.object({ params: z.object({
identityId: z.string() identityId: z.string()
}), }),
@@ -246,6 +265,12 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
method: "POST", method: "POST",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "Create Universal Auth Client Secret for identity",
security: [
{
bearerAuth: []
}
],
params: z.object({ params: z.object({
identityId: z.string() identityId: z.string()
}), }),
@@ -291,6 +316,12 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
method: "GET", method: "GET",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "List Universal Auth Client Secrets for identity",
security: [
{
bearerAuth: []
}
],
params: z.object({ params: z.object({
identityId: z.string() identityId: z.string()
}), }),
@@ -327,6 +358,12 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
method: "POST", method: "POST",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "Revoke Universal Auth Client Secrets for identity",
security: [
{
bearerAuth: []
}
],
params: z.object({ params: z.object({
identityId: z.string(), identityId: z.string(),
clientSecretId: z.string() clientSecretId: z.string()
@@ -10,6 +10,13 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
url: "/:workspaceId/environments", url: "/:workspaceId/environments",
method: "POST", method: "POST",
schema: { schema: {
description: "Create environment",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
workspaceId: z.string().trim() workspaceId: z.string().trim()
}), }),
@@ -58,6 +65,13 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
url: "/:workspaceId/environments/:id", url: "/:workspaceId/environments/:id",
method: "PATCH", method: "PATCH",
schema: { schema: {
description: "Update environment",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
id: z.string().trim() id: z.string().trim()
@@ -114,6 +128,13 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
url: "/:workspaceId/environments/:id", url: "/:workspaceId/environments/:id",
method: "DELETE", method: "DELETE",
schema: { schema: {
description: "Delete environment",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
id: z.string().trim() id: z.string().trim()
@@ -10,6 +10,13 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
url: "/:workspaceId/memberships", url: "/:workspaceId/memberships",
method: "GET", method: "GET",
schema: { schema: {
description: "Return project user memberships",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
workspaceId: z.string().trim() workspaceId: z.string().trim()
}), }),
@@ -96,6 +103,13 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
url: "/:workspaceId/memberships/:membershipId", url: "/:workspaceId/memberships/:membershipId",
method: "PATCH", method: "PATCH",
schema: { schema: {
description: "Update project user membership",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
membershipId: z.string().trim() membershipId: z.string().trim()
@@ -141,6 +155,13 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
url: "/:workspaceId/memberships/:membershipId", url: "/:workspaceId/memberships/:membershipId",
method: "DELETE", method: "DELETE",
schema: { schema: {
description: "Delete project user membership",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
membershipId: z.string().trim() membershipId: z.string().trim()
@@ -11,6 +11,13 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
url: "/", url: "/",
method: "POST", method: "POST",
schema: { schema: {
description: "Create folders",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
body: z.object({ body: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
@@ -57,6 +64,13 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
url: "/:folderId", url: "/:folderId",
method: "PATCH", method: "PATCH",
schema: { schema: {
description: "Update folder",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
// old way this was name // old way this was name
folderId: z.string() folderId: z.string()
@@ -109,6 +123,13 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
url: "/:folderId", url: "/:folderId",
method: "DELETE", method: "DELETE",
schema: { schema: {
description: "Delete a folder",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
folderId: z.string() folderId: z.string()
}), }),
@@ -158,6 +179,13 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
url: "/", url: "/",
method: "GET", method: "GET",
schema: { schema: {
description: "Get folders",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
querystring: z.object({ querystring: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
@@ -11,6 +11,13 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
url: "/", url: "/",
method: "POST", method: "POST",
schema: { schema: {
description: "Create secret imports",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
body: z.object({ body: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
@@ -65,6 +72,13 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
url: "/:secretImportId", url: "/:secretImportId",
method: "PATCH", method: "PATCH",
schema: { schema: {
description: "Update secret imports",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
secretImportId: z.string().trim() secretImportId: z.string().trim()
}), }),
@@ -128,6 +142,13 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
url: "/:secretImportId", url: "/:secretImportId",
method: "DELETE", method: "DELETE",
schema: { schema: {
description: "Delete secret imports",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
secretImportId: z.string().trim() secretImportId: z.string().trim()
}), }),
@@ -181,6 +202,13 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
url: "/", url: "/",
method: "GET", method: "GET",
schema: { schema: {
description: "Get secret imports",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
querystring: z.object({ querystring: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
@@ -10,6 +10,13 @@ export const registerIdentityOrgRouter = async (server: FastifyZodProvider) => {
url: "/:orgId/identity-memberships", url: "/:orgId/identity-memberships",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "Return organization identity memberships",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
orgId: z.string().trim() orgId: z.string().trim()
}), }),
@@ -46,6 +46,12 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
url: "/:projectId/identity-memberships/:identityId", url: "/:projectId/identity-memberships/:identityId",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "Update project identity memberships",
security: [
{
bearerAuth: []
}
],
params: z.object({ params: z.object({
projectId: z.string().trim(), projectId: z.string().trim(),
identityId: z.string().trim() identityId: z.string().trim()
@@ -77,6 +83,12 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
url: "/:projectId/identity-memberships/:identityId", url: "/:projectId/identity-memberships/:identityId",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "Delete project identity memberships",
security: [
{
bearerAuth: []
}
],
params: z.object({ params: z.object({
projectId: z.string().trim(), projectId: z.string().trim(),
identityId: z.string().trim() identityId: z.string().trim()
@@ -104,6 +116,12 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
url: "/:projectId/identity-memberships", url: "/:projectId/identity-memberships",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "Return project identity memberships",
security: [
{
bearerAuth: []
}
],
params: z.object({ params: z.object({
projectId: z.string().trim() projectId: z.string().trim()
}), }),
@@ -9,6 +9,13 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
method: "GET", method: "GET",
url: "/:organizationId/memberships", url: "/:organizationId/memberships",
schema: { schema: {
description: "Return organization user memberships",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
organizationId: z.string().trim() organizationId: z.string().trim()
}), }),
@@ -46,6 +53,13 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
method: "GET", method: "GET",
url: "/:organizationId/workspaces", url: "/:organizationId/workspaces",
schema: { schema: {
description: "Return projects in organization that user is part of",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
organizationId: z.string().trim() organizationId: z.string().trim()
}), }),
@@ -84,6 +98,13 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
method: "PATCH", method: "PATCH",
url: "/:organizationId/memberships/:membershipId", url: "/:organizationId/memberships/:membershipId",
schema: { schema: {
description: "Update organization user memberships",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ organizationId: z.string().trim(), membershipId: z.string().trim() }), params: z.object({ organizationId: z.string().trim(), membershipId: z.string().trim() }),
body: z.object({ body: z.object({
role: z.string().trim() role: z.string().trim()
@@ -113,6 +134,13 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
method: "DELETE", method: "DELETE",
url: "/:organizationId/memberships/:membershipId", url: "/:organizationId/memberships/:membershipId",
schema: { schema: {
description: "Delete organization user memberships",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ organizationId: z.string().trim(), membershipId: z.string().trim() }), params: z.object({ organizationId: z.string().trim(), membershipId: z.string().trim() }),
response: { response: {
200: z.object({ 200: z.object({
@@ -10,6 +10,12 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
url: "/:workspaceId/encrypted-key", url: "/:workspaceId/encrypted-key",
method: "GET", method: "GET",
schema: { schema: {
description: "Return encrypted project key",
security: [
{
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
workspaceId: z.string().trim() workspaceId: z.string().trim()
}), }),
@@ -21,6 +21,12 @@ export const registerServiceTokenRouter = async (server: FastifyZodProvider) =>
method: "GET", method: "GET",
onRequest: verifyAuth([AuthMode.SERVICE_TOKEN]), onRequest: verifyAuth([AuthMode.SERVICE_TOKEN]),
schema: { schema: {
description: "Return Infisical Token data",
security: [
{
bearerAuth: []
}
],
response: { response: {
200: ServiceTokensSchema.merge( 200: ServiceTokensSchema.merge(
z.object({ z.object({
@@ -71,6 +71,12 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
method: "GET", method: "GET",
url: "/me/organizations", url: "/me/organizations",
schema: { schema: {
description: "Return organizations that current user is part of",
security: [
{
apiKeyAuth: []
}
],
response: { response: {
200: z.object({ 200: z.object({
organizations: OrganizationsSchema.array() organizations: OrganizationsSchema.array()
@@ -179,6 +185,12 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
method: "GET", method: "GET",
url: "/me", url: "/me",
schema: { schema: {
description: "Retrieve the current user on the request",
security: [
{
apiKeyAuth: []
}
],
response: { response: {
200: z.object({ 200: z.object({
user: UsersSchema.merge(UserEncryptionKeysSchema.omit({ verifier: true })) user: UsersSchema.merge(UserEncryptionKeysSchema.omit({ verifier: true }))
@@ -38,6 +38,13 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
url: "/raw", url: "/raw",
method: "GET", method: "GET",
schema: { schema: {
description: "List secrets",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
querystring: z.object({ querystring: z.object({
workspaceId: z.string().trim().optional(), workspaceId: z.string().trim().optional(),
environment: z.string().trim().optional(), environment: z.string().trim().optional(),
@@ -121,6 +128,13 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
url: "/raw/:secretName", url: "/raw/:secretName",
method: "GET", method: "GET",
schema: { schema: {
description: "Get a secret by name",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
secretName: z.string().trim() secretName: z.string().trim()
}), }),
@@ -204,6 +218,13 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
url: "/raw/:secretName", url: "/raw/:secretName",
method: "POST", method: "POST",
schema: { schema: {
description: "Create secret",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
secretName: z.string().trim() secretName: z.string().trim()
}), }),
@@ -274,6 +295,13 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
url: "/raw/:secretName", url: "/raw/:secretName",
method: "PATCH", method: "PATCH",
schema: { schema: {
description: "Update secret",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
secretName: z.string().trim() secretName: z.string().trim()
}), }),
@@ -341,6 +369,13 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
url: "/raw/:secretName", url: "/raw/:secretName",
method: "DELETE", method: "DELETE",
schema: { schema: {
description: "Delete secret",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
secretName: z.string().trim() secretName: z.string().trim()
}), }),
@@ -159,6 +159,17 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => {
userAgent userAgent
}); });
void server.services.telemetry.sendLoopsEvent(user.email, user.firstName || "", user.lastName || "");
void server.services.telemetry.sendPostHogEvents({
event: PostHogEventTypes.UserSignedUp,
distinctId: user.email,
properties: {
email: user.email,
attributionSource: "Team Invite"
}
});
void res.setCookie("jid", refreshToken, { void res.setCookie("jid", refreshToken, {
httpOnly: true, httpOnly: true,
path: "/", path: "/",
+80 -82
View File
@@ -1,4 +1,4 @@
version: '3' version: "3.9"
services: services:
nginx: nginx:
@@ -10,36 +10,84 @@ services:
volumes: volumes:
- ./nginx/default.dev.conf:/etc/nginx/conf.d/default.conf:ro - ./nginx/default.dev.conf:/etc/nginx/conf.d/default.conf:ro
depends_on: depends_on:
- frontend
- backend - backend
networks: - frontend
- infisical-dev
backend: db:
container_name: infisical-dev-backend image: postgres:14-alpine
restart: unless-stopped ports:
- "5432:5432"
volumes:
- postgres-data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: infisical
POSTGRES_USER: infisical
POSTGRES_DB: infisical
redis:
image: redis
container_name: infisical-dev-redis
environment:
- ALLOW_EMPTY_PASSWORD=yes
ports:
- 6379:6379
volumes:
- redis_data:/data
redis-commander:
container_name: infisical-dev-redis-commander
image: rediscommander/redis-commander
restart: always
depends_on: depends_on:
- mongo
- smtp-server
- redis - redis
environment:
- REDIS_HOSTS=local:redis:6379
ports:
- "8085:8081"
db-test:
profiles: ["test"]
image: postgres:14-alpine
ports:
- "5430:5432"
environment:
POSTGRES_PASSWORD: infisical
POSTGRES_USER: infisical
POSTGRES_DB: infisical-test
db-migration:
container_name: infisical-db-migration
depends_on:
- db
build: build:
context: ./backend context: ./backend
dockerfile: Dockerfile dockerfile: Dockerfile.dev
volumes:
- ./backend/src:/app/src
- ./backend/nodemon.json:/app/nodemon.json
- /app/node_modules
- ./backend/api-documentation.json:/app/api-documentation.json
- ./backend/swagger.ts:/app/swagger.ts
command: npm run dev
env_file: .env env_file: .env
environment:
- DB_CONNECTION_URI=postgres://infisical:infisical@db/infisical?sslmode=disable
command: npm run migration:latest
backend:
container_name: infisical-dev-api
build:
context: ./backend
dockerfile: Dockerfile.dev
depends_on:
db:
condition: service_started
redis:
condition: service_started
db-migration:
condition: service_completed_successfully
env_file:
- .env
ports:
- 4000:4000
environment: environment:
- NODE_ENV=development - NODE_ENV=development
- MONGO_URL=mongodb://root:example@mongo:27017/?authSource=admin - DB_CONNECTION_URI=postgres://infisical:infisical@db/infisical?sslmode=disable
networks: volumes:
- infisical-dev - ./backend/src:/app/src
extra_hosts:
- "host.docker.internal:host-gateway"
frontend: frontend:
container_name: infisical-dev-frontend container_name: infisical-dev-frontend
@@ -55,81 +103,31 @@ services:
env_file: .env env_file: .env
environment: environment:
- NEXT_PUBLIC_ENV=development - NEXT_PUBLIC_ENV=development
- INFISICAL_TELEMETRY_ENABLED=${TELEMETRY_ENABLED} - INFISICAL_TELEMETRY_ENABLED=false
networks:
- infisical-dev
mongo: pgadmin:
image: mongo image: dpage/pgadmin4
container_name: infisical-dev-mongo
restart: always restart: always
env_file: .env
environment: environment:
- MONGO_INITDB_ROOT_USERNAME=root PGADMIN_DEFAULT_EMAIL: [email protected]
- MONGO_INITDB_ROOT_PASSWORD=example PGADMIN_DEFAULT_PASSWORD: pass
volumes:
- mongo-data:/data/db
networks:
- infisical-dev
mongo-express:
container_name: infisical-dev-mongo-express
image: mongo-express
restart: always
depends_on:
- mongo
env_file: .env
environment:
- ME_CONFIG_MONGODB_ADMINUSERNAME=root
- ME_CONFIG_MONGODB_ADMINPASSWORD=example
- ME_CONFIG_MONGODB_URL=mongodb://root:example@mongo:27017/
ports: ports:
- 8081:8081 - 5050:80
networks: depends_on:
- infisical-dev - db
smtp-server: smtp-server:
container_name: infisical-dev-smtp-server container_name: infisical-dev-smtp-server
image: lytrax/mailhog:latest # https://github.com/mailhog/MailHog/issues/353#issuecomment-821137362 image: lytrax/mailhog:latest # https://github.com/mailhog/MailHog/issues/353#issuecomment-821137362
restart: always restart: always
logging: logging:
driver: 'none' # disable saving logs driver: "none" # disable saving logs
ports: ports:
- 1025:1025 # SMTP server - 1025:1025 # SMTP server
- 8025:8025 # Web UI - 8025:8025 # Web UI
networks:
- infisical-dev
redis:
image: redis
container_name: infisical-dev-redis
environment:
- ALLOW_EMPTY_PASSWORD=yes
ports:
- 6379:6379
volumes:
- redis_data:/data
networks:
- infisical-dev
redis-commander:
container_name: infisical-dev-redis-commander
image: rediscommander/redis-commander
restart: always
depends_on:
- redis
environment:
- REDIS_HOSTS=local:redis:6379
ports:
- "8085:8081"
networks:
- infisical-dev
volumes: volumes:
mongo-data: postgres-data:
driver: local driver: local
redis_data: redis_data:
driver: local driver: local
networks:
infisical-dev:
-146
View File
@@ -1,146 +0,0 @@
version: "3.9"
services:
nginx:
container_name: infisical-dev-nginx
image: nginx
restart: always
ports:
- 8080:80
volumes:
- ./nginx/default.dev.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- backend
- frontend
db:
image: postgres:14-alpine
ports:
- "5432:5432"
volumes:
- postgres-data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: infisical
POSTGRES_USER: infisical
POSTGRES_DB: infisical
redis:
image: redis
container_name: infisical-dev-redis
environment:
- ALLOW_EMPTY_PASSWORD=yes
ports:
- 6379:6379
volumes:
- redis_data:/data
redis-commander:
container_name: infisical-dev-redis-commander
image: rediscommander/redis-commander
restart: always
depends_on:
- redis
environment:
- REDIS_HOSTS=local:redis:6379
ports:
- "8085:8081"
db-test:
profiles: ["test"]
image: postgres:14-alpine
ports:
- "5430:5432"
environment:
POSTGRES_PASSWORD: infisical
POSTGRES_USER: infisical
POSTGRES_DB: infisical-test
backend:
container_name: infisical-dev-api
build:
context: ./backend
dockerfile: Dockerfile.dev
depends_on:
- db
- redis
env_file:
- .env
ports:
- 4000:4000
environment:
- NODE_ENV=development
- DB_CONNECTION_URI=postgres://infisical:infisical@db/infisical?sslmode=disable
volumes:
- ./backend/src:/app/src
frontend:
container_name: infisical-dev-frontend
restart: unless-stopped
depends_on:
- backend
build:
context: ./frontend
dockerfile: Dockerfile.dev
volumes:
- ./frontend/src:/app/src/ # mounted whole src to avoid missing reload on new files
- ./frontend/public:/app/public
env_file: .env
environment:
- NEXT_PUBLIC_ENV=development
- INFISICAL_TELEMETRY_ENABLED=false
pgadmin:
image: dpage/pgadmin4
restart: always
environment:
PGADMIN_DEFAULT_EMAIL: [email protected]
PGADMIN_DEFAULT_PASSWORD: pass
ports:
- 5050:80
depends_on:
- db
smtp-server:
container_name: infisical-dev-smtp-server
image: lytrax/mailhog:latest # https://github.com/mailhog/MailHog/issues/353#issuecomment-821137362
restart: always
logging:
driver: "none" # disable saving logs
ports:
- 1025:1025 # SMTP server
- 8025:8025 # Web UI
# mongo:
# image: mongo
# container_name: infisical-dev-mongo
# restart: always
# env_file: .env
# environment:
# - MONGO_INITDB_ROOT_USERNAME=root
# - MONGO_INITDB_ROOT_PASSWORD=example
# volumes:
# - mongo-data:/data/db
# ports:
# - 27017:27017
#
# mongo-express:
# container_name: infisical-dev-mongo-express
# image: mongo-express
# restart: always
# depends_on:
# - mongo
# env_file: .env
# environment:
# - ME_CONFIG_MONGODB_ADMINUSERNAME=root
# - ME_CONFIG_MONGODB_ADMINPASSWORD=example
# - ME_CONFIG_MONGODB_URL=mongodb://root:example@mongo:27017/
# ports:
# - 8081:8081
volumes:
postgres-data:
driver: local
redis_data:
driver: local
mongo-data:
driver: local
+22 -10
View File
@@ -1,12 +1,27 @@
version: "3" version: "3"
services: services:
db-migration:
container_name: infisical-db-migration
depends_on:
- db
image: infisical/infisical:latest-postgres
env_file: .env
command: npm run migration:latest
networks:
- infisical
backend: backend:
container_name: infisical-backend container_name: infisical-backend
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
- mongo db:
image: infisical/infisical:latest condition: service_started
redis:
condition: service_started
db-migration:
condition: service_completed_successfully
image: infisical/infisical:latest-postgres
env_file: .env env_file: .env
ports: ports:
- 80:8080 - 80:8080
@@ -28,21 +43,18 @@ services:
volumes: volumes:
- redis_data:/data - redis_data:/data
mongo: db:
container_name: infisical-mongo container_name: infisical-db
image: mongo image: postgres:14-alpine
restart: always restart: always
env_file: .env env_file: .env
environment:
- MONGO_INITDB_ROOT_USERNAME=${MONGO_USERNAME}
- MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD}
volumes: volumes:
- mongo-data:/data/db - pg_data:/data/db
networks: networks:
- infisical - infisical
volumes: volumes:
mongo-data: pg_data:
driver: local driver: local
redis_data: redis_data:
driver: local driver: local
@@ -1,4 +1,4 @@
--- ---
title: "Create" title: "Create"
openapi: "POST /api/v2/workspace/{workspaceId}/environments" openapi: "POST /api/v1/workspace/{workspaceId}/environments"
--- ---
@@ -1,4 +1,4 @@
--- ---
title: "Delete" title: "Delete"
openapi: "DELETE /api/v2/workspace/{workspaceId}/environments" openapi: "DELETE /api/v1/workspace/{workspaceId}/environments/{id}"
--- ---
@@ -1,4 +1,4 @@
--- ---
title: "Update" title: "Update"
openapi: "PUT /api/v2/workspace/{workspaceId}/environments" openapi: "PATCH /api/v1/workspace/{workspaceId}/environments/{id}"
--- ---
@@ -1,4 +1,4 @@
--- ---
title: "Create" title: "Create"
openapi: "POST /api/v1/folders/" openapi: "POST /api/v1/folders"
--- ---
@@ -1,4 +1,4 @@
--- ---
title: "Delete" title: "Delete"
openapi: "DELETE /api/v1/folders/{folderName}" openapi: "DELETE /api/v1/folders/{folderId}"
--- ---
@@ -1,4 +1,4 @@
--- ---
title: "List" title: "List"
openapi: "GET /api/v1/folders/" openapi: "GET /api/v1/folders"
--- ---
@@ -1,4 +1,4 @@
--- ---
title: "Update" title: "Update"
openapi: "PATCH /api/v1/folders/{folderName}" openapi: "PATCH /api/v1/folders/{folderId}"
--- ---
@@ -1,4 +1,4 @@
--- ---
title: "Create" title: "Create"
openapi: "POST /api/v1/identities/" openapi: "POST /api/v1/identities"
--- ---
@@ -1,4 +1,4 @@
--- ---
title: "Delete" title: "Delete"
openapi: "DELETE /api/v1/identities/{identityId}" openapi: "DELETE /api/v1/identities/{identityId}"
--- ---
@@ -1,4 +1,4 @@
--- ---
title: "Update" title: "Update"
openapi: "PATCH /api/v1/identities/{identityId}" openapi: "PATCH /api/v1/identities/{identityId}"
--- ---
@@ -1,4 +1,4 @@
--- ---
title: "List Identity Memberships" title: "List Identity Memberships"
openapi: "GET /api/v2/organizations/{organizationId}/identity-memberships" openapi: "GET /api/v2/organizations/{orgId}/identity-memberships"
--- ---
@@ -1,4 +1,4 @@
--- ---
title: "Create" title: "Create"
openapi: "POST /api/v1/secret-imports/" openapi: "POST /api/v1/secret-imports"
--- ---
@@ -1,4 +1,4 @@
--- ---
title: "Delete" title: "Delete"
openapi: "DELETE /api/v1/secret-imports/{id}" openapi: "DELETE /api/v1/secret-imports/{secretImportId}"
--- ---
@@ -1,4 +1,4 @@
--- ---
title: "List" title: "List"
openapi: "GET /api/v1/secret-imports/" openapi: "GET /api/v1/secret-imports"
--- ---
@@ -1,4 +1,4 @@
--- ---
title: "Update" title: "Update"
openapi: "PUT /api/v1/secret-imports/{id}" openapi: "PATCH /api/v1/secret-imports/{secretImportId}"
--- ---
@@ -1,6 +1,6 @@
--- ---
title: "Get" title: "Get"
openapi: "GET /api/v2/service-token/" openapi: "GET /api/v2/service-token"
--- ---
<Warning> <Warning>
@@ -1,4 +1,4 @@
--- ---
title: "Delete Identity Membership" title: "Delete Identity Membership"
openapi: "DELETE /api/v2/workspace/{workspaceId}/identity-memberships/{identityId}" openapi: "DELETE /api/v2/workspace/{projectId}/identity-memberships/{identityId}"
--- ---
@@ -1,4 +1,4 @@
--- ---
title: "Delete User Membership" title: "Delete User Membership"
openapi: "DELETE /api/v2/workspace/{workspaceId}/memberships/{membershipId}" openapi: "DELETE /api/v1/workspace/{workspaceId}/memberships/{membershipId}"
--- ---
@@ -1,4 +1,4 @@
--- ---
title: "List Identity Memberships" title: "List Identity Memberships"
openapi: "GET /api/v2/workspace/{workspaceId}/identity-memberships" openapi: "GET /api/v2/workspace/{projectId}/identity-memberships"
--- ---
@@ -1,4 +1,4 @@
--- ---
title: "Get User Memberships" title: "Get User Memberships"
openapi: "GET /api/v2/workspace/{workspaceId}/memberships" openapi: "GET /api/v1/workspace/{workspaceId}/memberships"
--- ---
@@ -1,4 +1,4 @@
--- ---
title: "Roll Back to Snapshot" title: "Roll Back to Snapshot"
openapi: "POST /api/v1/workspace/{workspaceId}/secret-snapshots/rollback" openapi: "POST /api/v1/secret-snapshot/{secretSnapshotId}/rollback"
--- ---
@@ -1,4 +1,4 @@
--- ---
title: "Update Identity Membership" title: "Update Identity Membership"
openapi: "PATCH /api/v2/workspace/{workspaceId}/identity-memberships/{identityId}" openapi: "PATCH /api/v2/workspace/{projectId}/identity-memberships/{identityId}"
--- ---
@@ -1,4 +1,4 @@
--- ---
title: "Update User Membership" title: "Update User Membership"
openapi: "PATCH /api/v2/workspace/{workspaceId}/memberships/{membershipId}" openapi: "PATCH /api/v1/workspace/{workspaceId}/memberships/{membershipId}"
--- ---
@@ -0,0 +1,82 @@
---
title: 'Backend folder structure'
---
```
├── scripts
├── e2e-test
└── src/
├── @types/
│ ├── knex.d.ts
│ └── fastify.d.ts
├── db/
│ ├── migrations
│ ├── schemas
│ └── seed
├── lib/
│ ├── fn
│ ├── date
│ └── config
├── queue
├── server/
│ ├── routes/
│ │ ├── v1
│ │ └── v2
│ ├── plugins
│ └── config
├── services/
│ ├── auth
│ ├── org
│ └── project/
│ ├── project-service.ts
│ ├── project-types.ts
│ └── project-dal.ts
└── ee/
├── routes
└── services
```
### `backend/scripts`
Contains reusable scripts for backend automation, like running migrations and generating SQL schemas.
### `backend/e2e-test`
Integration tests for the APIs.
### `backend/src`
The source code of the backend.
- `@types`: Type definitions for libraries like Fastify and Knex.
- `db`: Knex.js configuration for the database, including migration, seed files, and SQL type schemas.
- `lib`: Stateless, reusable functions used across the codebase.
- `queue`: Infisical's queue system based on BullMQ.
### `src/server`
- Scope anything related to Fastify/service here.
- Includes routes, Fastify plugins, and server configurations.
- The routes folder contains various versions of routes separated into v1, v2, etc.
### `src/services`
- Handles the core business logic for all operations.
- Follows the co-location principle: related components should be kept together.
- Each service component typically contains:
1. **dal**: Database Access Layer functions for database operations
2. **service**: The service layer containing business logic.
3. **type**: Type definitions used within the service component.
4. **fns**: An optional component for sharing reusable functions related to the service.
5. **queue**: An optional component for queue-specific logic, like `secret-queue.ts`.
### `src/ee`
Follows the same pattern as above, with the exception of a license change from MIT to Infisical Proprietary License.
### Guidelines and Best Practices
- All services are interconnected at `/src/server/routes/index.ts`, following the principle of simple dependency injection.
- Files should be named in dash-case.
- Avoid using classes in the codebase; opt for simple functions instead.
- All committed code must be properly linted using `npm run lint:fix` and type-checked with `npm run type:check`.
- Minimize shared logic between services as much as possible.
- Controllers within a router component should ideally call only one service layer, with exceptions for services like `audit-log` that require access to request object data.
@@ -0,0 +1,56 @@
---
title: "Backend development guide"
---
Suppose you're interested in implementing a new feature in Infisical's backend, let's call it "feature-x." Here are the general steps you should follow.
## Database schema migration
In order to run [schema migrations](https://en.wikipedia.org/wiki/Schema_migration#:~:text=A%20schema%20migration%20is%20performed,some%20newer%20or%20older%20version) you need to expose your database connection string. Create a `.env.migration` file to set the database connection URI for migration scripts, or alternatively, export the `DB_CONNECTION_URI` environment variable.
## Creating new database model
If your feature involves a change in the database, you need to first address this by generating the necessary database schemas.
1. If you're adding a new table, update the `TableName` enum in `/src/db/schemas/models.ts` to include the new table name.
2. Create a new migration file by running `npm run migration:new` and give it a relevant name, such as `feature-x`.
3. Navigate to `/src/db/migrations/<timestamp>_<feature-x>.ts`.
4. Modify both the `up` and `down` functions to create or alter Postgres fields on migration up and to revert these changes on migration down, ensuring idempotency as outlined [here](https://github.com/graphile/migrate/blob/main/docs/idempotent-examples.md).
### Generating TS Schemas
While typically you would need to manually write TS types for Knex type-sense, we have automated this process:
1. Start the server.
2. Run `npm run migration:latest` to apply all database changes.
3. Execute `npm run generate:schema` to automatically generate types and schemas using [zod](https://github.com/colinhacks/zod) in the `/src/db/schemas` folder.
4. Update the barrel export in `schema/index` and include the new tables in `/src/@types/knex.d.ts` to enable type-sensing in Knex.js.
## Business Logic
Once the database changes are in place, it's time to create the APIs for `feature-x`:
1. Execute `npm run generate:component`.
2. Choose option 1 for the service component.
3. Name the service in dash-case, like `feature-x`. This will create a `feature-x` folder in `/src/services` containing three files.
1. `feature-x-dal`: The Database Access Layer functions.
2. `feature-x-service`: The service layer where all the business logic is handled.
3. `feature-x-type`: The types used by `feature-x`.
For reusable shared functions, set up a file named `feature-x-fns`.
Use the custom Infisical function `ormify` in `src/lib/knex` for simple database operations within the DAL.
## Connecting the Service Layer to the Server Layer
Server-related logic is handled in `/src/server`. To connect the service layer to the server layer, we use Fastify plugins for dependency injection:
1. Add the service type in the `fastify.d.ts` file under the `service` namespace of a FastifyServerInstance type.
2. In `/src/server/routes/index.ts`, instantiate the required dependencies for `feature-x`, such as the DAL and service layers, and then pass them to `fastify.register("service,{...dependencies})`.
3. This makes the service layer accessible within all routes under the Fastify service instance, accessed via `server.services.<registered service name>.<function>`.
## Writing API Routes
1. To create a route component, run `npm generate:component`.
2. Select option 3, type the router name in dash-case, and provide the version number. This will generate a router file in `src/server/routes/v<version-number>/<router component name>`
1. Implement your logic to connect with the service layer as needed.
2. Import the router component in the version folder's index.ts. For instance, if it's in v1, import it in `v1/index.ts`.
3. Finally, register it under the appropriate prefix for access.
@@ -4,7 +4,7 @@ description: "Programmatically interact with Infisical"
--- ---
<Note> <Note>
Currently, identities can only be used to make authenticated requests to the Infisical API and SDKs. They do not work with clients such as CLI, K8s Operator, Terraform Provider, etc. Currently, identities can only be used to make authenticated requests to the Infisical API, SDKs, and Agent. They do not work with clients such as CLI, K8s Operator, Terraform Provider, etc.
We will be releasing compatibility with it across clients in the coming quarter. We will be releasing compatibility with it across clients in the coming quarter.
</Note> </Note>
@@ -50,4 +50,4 @@ Check out the following authentication method-specific guides for step-by-step i
- The identity you are trying to read, update, or delete is more privileged than yourself. - The identity you are trying to read, update, or delete is more privileged than yourself.
- The role you are trying to create an identity for or update an identity to is more privileged than yours. - The role you are trying to create an identity for or update an identity to is more privileged than yours.
</Accordion> </Accordion>
</AccordionGroup> </AccordionGroup>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

After

Width:  |  Height:  |  Size: 324 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 235 KiB

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 KiB

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 429 KiB

After

Width:  |  Height:  |  Size: 431 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 732 KiB

+35 -18
View File
@@ -3,22 +3,32 @@ title: "Jenkins"
description: "How to effectively and securely manage secrets in Jenkins using Infisical" description: "How to effectively and securely manage secrets in Jenkins using Infisical"
--- ---
**Objective**: Fetch secrets from Infisical to Jenkins pipelines
In this guide, we'll outline the steps to deliver secrets from Infisical to Jenkins via the Infisical CLI.
At a high level, the Infisical CLI will be executed within your build environment and use a service token to authenticate with Infisical.
This token must be added as a Jenkins Credential and then passed to the Infisical CLI as an environment variable, enabling it to access and retrieve secrets within your workflows.
Prerequisites: Prerequisites:
- Set up and add secrets to [Infisical](https://app.infisical.com). - Set up and add secrets to [Infisical](https://app.infisical.com).
- You have a working Jenkins installation with the [credentials plugin](https://plugins.jenkins.io/credentials/) installed. - You have a working Jenkins installation with the [credentials plugin](https://plugins.jenkins.io/credentials/) installed.
- You have the Infisical CLI installed on your Jenkins executor nodes or container images. - You have the [Infisical CLI](/cli/overview) installed on your Jenkins executor nodes or container images.
## Add Infisical Service Token to Jenkins ## Add Infisical Service Token to Jenkins
After setting up your project in Infisical and adding the Infisical CLI to container images, you will need to add the Infisical Service Token to Jenkins. Once you have generated the token, browse to **Manage Jenkins > Manage Credentials** in your Jenkins installation. After setting up your project in Infisical and installing the Infisical CLI to the environment where your Jenkins builds will run, you will need to add the Infisical Service Token to Jenkins.
To generate a Infisical service token, follow the guide [here](/documentation/platform/token).
Once you have generated the token, navigate to **Manage Jenkins > Manage Credentials** in your Jenkins instance.
![Jenkins step 1](../../images/integrations/jenkins/jenkins_1.png) ![Jenkins step 1](../../images/integrations/jenkins/jenkins_1.png)
Click on the credential store you want to store the Infisical Service Token in. In this case, we're using the default Jenkins global store. Click on the credential store you want to store the Infisical Service Token in. In this case, we're using the default Jenkins global store.
<Info> <Info>
Each of your projects will have a different INFISICAL_SERVICE_TOKEN though. Each of your projects will have a different `INFISICAL_TOKEN`.
As a result, it may make sense to spread these out into separate credential domains depending on your use case. As a result, it may make sense to spread these out into separate credential domains depending on your use case.
</Info> </Info>
@@ -28,18 +38,22 @@ Now, click Add Credentials.
![Jenkins step 3](../../images/integrations/jenkins/jenkins_3.png) ![Jenkins step 3](../../images/integrations/jenkins/jenkins_3.png)
Choose **Secret text** from the **Kind** dropdown menu, paste the Infisical Service Token into the **Secret** field, enter `INFISICAL_SERVICE_TOKEN` into the **Description** field, and click **OK**. Choose **Secret text** for the **Kind** option from the dropdown list and enter the Infisical Service Token in the **Secret** field.
Although the **ID** can be any value, we'll set it to `infisical-service-token` for the sake of this guide.
The description is optional and can be any text you prefer.
![Jenkins step 4](../../images/integrations/jenkins/jenkins_4.png) ![Jenkins step 4](../../images/integrations/jenkins/jenkins_4.png)
When you're done, you should have a credential similar to the one below: When you're done, you should see a credential similar to the one below:
![Jenkins step 5](../../images/integrations/jenkins/jenkins_5.png) ![Jenkins step 5](../../images/integrations/jenkins/jenkins_5.png)
## Use Infisical in a Freestyle Project ## Use Infisical in a Freestyle Project
To use Infisical in a Freestyle Project job, you'll need to expose the credential you created above in an environment variable. First, click New Item from the dashboard navigation sidebar: To fetch secrets with Infisical in a Freestyle Project job, you'll need to expose the credential you created above as an environment variable to the Infisical CLI.
To do so, first click **New Item** from the dashboard navigation sidebar:
![Jenkins step 6](../../images/integrations/jenkins/jenkins_6.png) ![Jenkins step 6](../../images/integrations/jenkins/jenkins_6.png)
@@ -51,7 +65,8 @@ Scroll down to the **Build Environment** section and enable the **Use secret tex
![Jenkins step 8](../../images/integrations/jenkins/jenkins_8.png) ![Jenkins step 8](../../images/integrations/jenkins/jenkins_8.png)
Enter INFISICAL_SERVICE_TOKEN in the **Variable** field, select the **Specific credentials** option from the Credentials section and choose INFISICAL_SERVICE_TOKEN from the dropdown menu. Enter `INFISICAL_TOKEN` in the **Variable** field then click the **Specific credentials** option from the Credentials section and select the credential you created earlier.
In this case, we saved it as `Infisical service token` so we'll choose that from the dropdown menu.
![Jenkins step 9](../../images/integrations/jenkins/jenkins_9.png) ![Jenkins step 9](../../images/integrations/jenkins/jenkins_9.png)
@@ -59,15 +74,16 @@ Scroll down to the **Build** section and choose **Execute shell** from the **Add
![Jenkins step 10](../../images/integrations/jenkins/jenkins_10.png) ![Jenkins step 10](../../images/integrations/jenkins/jenkins_10.png)
In the command field, enter the following command and click **Save**: In the command field, you can now use the Infisical CLI to fetch secrets.
The example command below will print the secrets using the service token passed as a credential. When done, click **Save**.
``` ```
infisical run -- printenv infisical secrets --env=dev --path=/
``` ```
![Jenkins step 11](../../images/integrations/jenkins/jenkins_11.png) ![Jenkins step 11](../../images/integrations/jenkins/jenkins_11.png)
Finally, click **Build Now** from the navigation sidebar to test your new job. Finally, click **Build Now** from the navigation sidebar to run your new job.
<Info> <Info>
Running into issues? Join Infisical's [community Slack](https://infisical.com/slack) for quick support. Running into issues? Join Infisical's [community Slack](https://infisical.com/slack) for quick support.
@@ -77,7 +93,8 @@ Finally, click **Build Now** from the navigation sidebar to test your new job.
## Use Infisical in a Jenkins Pipeline ## Use Infisical in a Jenkins Pipeline
To use Infisical in a Pipeline job, you'll need to expose the credential you created above as an environment variable. First, click **New Item** from the dashboard navigation sidebar: To fetch secrets using Infisical in a Pipeline job, you'll need to expose the Jenkins credential you created above as an environment variable.
To do so, click **New Item** from the dashboard navigation sidebar:
![Jenkins step 6](../../images/integrations/jenkins/jenkins_6.png) ![Jenkins step 6](../../images/integrations/jenkins/jenkins_6.png)
@@ -92,31 +109,31 @@ pipeline {
agent any agent any
environment { environment {
INFISICAL_SERVICE_TOKEN = credentials('INFISICAL_SERVICE_TOKEN') INFISICAL_TOKEN = credentials('infisical-service-token')
} }
stages { stages {
stage('Run Infisical') { stage('Run Infisical') {
steps { steps {
sh("infisical secrets") sh("infisical secrets --env=dev --path=/")
// doesn't work // doesn't work
// sh("docker run --rm test-container infisical secrets") // sh("docker run --rm test-container infisical secrets")
// works // works
// sh("docker run -e INFISICAL_SERVICE_TOKEN=${INFISICAL_SERVICE_TOKEN} --rm test-container infisical secrets") // sh("docker run -e INFISICAL_TOKEN=${INFISICAL_TOKEN} --rm test-container infisical secrets --env=dev --path=/")
// doesn't work // doesn't work
// sh("docker-compose up -d") // sh("docker-compose up -d")
// works // works
// sh("INFISICAL_SERVICE_TOKEN=${INFISICAL_SERVICE_TOKEN} docker-compose up -d") // sh("INFISICAL_TOKEN=${INFISICAL_TOKEN} docker-compose up -d")
} }
} }
} }
} }
``` ```
This is a very basic sample that you can work from. Jenkins injects the INFISICAL_SERVICE_TOKEN environment variable defined in the pipeline into the shell the commands execute with, but there are some situations where that won't pass through properly – notably if you're executing docker containers on the executor machine. The examples above should give you some idea for how that will work. The example provided above serves as an initial guide. It shows how Jenkins adds the `INFISICAL_TOKEN` environment variable, which is configured in the pipeline, into the shell for executing commands.
There may be instances where this doesn't work as expected in the context of running Docker commands.
Finally, click **Build Now** from the navigation sidebar to test your new job. However, the list of working examples should provide some insight into how this can be handled properly.
+4 -1
View File
@@ -1,5 +1,6 @@
{ {
"name": "Infisical", "name": "Infisical",
"openapi": "https://app.infisical.com/api/docs/json",
"logo": { "logo": {
"dark": "/logo/dark.svg", "dark": "/logo/dark.svg",
"light": "/logo/light.svg", "light": "/logo/light.svg",
@@ -463,7 +464,9 @@
{ {
"group": "Contributing to platform", "group": "Contributing to platform",
"pages": [ "pages": [
"contributing/platform/developing" "contributing/platform/developing",
"contributing/platform/backend/how-to-create-a-feature",
"contributing/platform/backend/folder-structure"
] ]
}, },
{ {
-5164
View File
File diff suppressed because it is too large Load Diff
@@ -58,8 +58,8 @@ export default function DonwloadBackupPDFStep({
return ( return (
<div className="flex flex-col items-center w-full h-full md:px-6 mx-auto mb-36 md:mb-16"> <div className="flex flex-col items-center w-full h-full md:px-6 mx-auto mb-36 md:mb-16">
<p className="text-xl text-center font-medium flex justify-center text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200"> <p className="text-xl text-center font-medium flex flex-col justify-center items-center text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200">
<FontAwesomeIcon icon={faWarning} className="ml-2 mr-3 pt-1 text-2xl text-bunker-200" /> <FontAwesomeIcon icon={faWarning} className="ml-2 mr-3 pt-1 mb-6 text-6xl text-bunker-200" />
{t("signup.step4-message")} {t("signup.step4-message")}
</p> </p>
<div className="flex flex-col pb-2 bg-mineshaft-800 border border-mineshaft-600 items-center justify-center text-center lg:w-1/6 w-full md:min-w-[24rem] mt-8 max-w-md text-bunker-300 text-md rounded-md"> <div className="flex flex-col pb-2 bg-mineshaft-800 border border-mineshaft-600 items-center justify-center text-center lg:w-1/6 w-full md:min-w-[24rem] mt-8 max-w-md text-bunker-300 text-md rounded-md">
@@ -128,10 +128,10 @@ export const SignUpPage = () => {
animate={{ opacity: 1, translateX: 0 }} animate={{ opacity: 1, translateX: 0 }}
exit={{ opacity: 0, translateX: 30 }} exit={{ opacity: 0, translateX: 30 }}
> >
<div className="flex flex-col items-center space-y-4 text-center"> <div className="flex flex-col items-center space-y-2 text-center">
<img src="/images/gradientLogo.svg" height={90} width={120} alt="Infisical logo" /> <img src="/images/gradientLogo.svg" height={90} width={120} alt="Infisical logo" />
<div className="text-4xl">Welcome to Infisical</div> <div className="text-4xl pt-4">Welcome to Infisical</div>
<div>Create your first Admin Account</div> <div className="text-bunker-300 pb-4">Create your first Super Admin Account</div>
</div> </div>
<form onSubmit={handleSubmit(handleFormSubmit)}> <form onSubmit={handleSubmit(handleFormSubmit)}>
<div className="mt-8"> <div className="mt-8">
@@ -145,7 +145,7 @@ export const SignUpPage = () => {
errorText={error?.message} errorText={error?.message}
isError={Boolean(error)} isError={Boolean(error)}
> >
<Input isFullWidth size="lg" {...field} /> <Input isFullWidth size="md" {...field} />
</FormControl> </FormControl>
)} )}
/> />
@@ -158,7 +158,7 @@ export const SignUpPage = () => {
errorText={error?.message} errorText={error?.message}
isError={Boolean(error)} isError={Boolean(error)}
> >
<Input isFullWidth size="lg" {...field} /> <Input isFullWidth size="md" {...field} />
</FormControl> </FormControl>
)} )}
/> />
@@ -168,7 +168,7 @@ export const SignUpPage = () => {
name="email" name="email"
render={({ field, fieldState: { error } }) => ( render={({ field, fieldState: { error } }) => (
<FormControl label="Email" errorText={error?.message} isError={Boolean(error)}> <FormControl label="Email" errorText={error?.message} isError={Boolean(error)}>
<Input isFullWidth size="lg" {...field} /> <Input isFullWidth size="md" {...field} />
</FormControl> </FormControl>
)} )}
/> />
@@ -181,7 +181,7 @@ export const SignUpPage = () => {
errorText={error?.message} errorText={error?.message}
isError={Boolean(error)} isError={Boolean(error)}
> >
<Input isFullWidth size="lg" type="password" {...field} /> <Input isFullWidth size="md" type="password" {...field} />
</FormControl> </FormControl>
)} )}
/> />
@@ -194,13 +194,13 @@ export const SignUpPage = () => {
errorText={error?.message} errorText={error?.message}
isError={Boolean(error)} isError={Boolean(error)}
> >
<Input isFullWidth size="lg" type="password" {...field} /> <Input isFullWidth size="md" type="password" {...field} />
</FormControl> </FormControl>
)} )}
/> />
</div> </div>
<Button type="submit" isFullWidth className="mt-4" isLoading={isSubmitting}> <Button type="submit" colorSchema="primary" variant="outline_bg" isFullWidth className="mt-4" isLoading={isSubmitting}>
Let&apos;s Go Continue
</Button> </Button>
</form> </form>
</motion.div> </motion.div>
@@ -15,8 +15,8 @@ export const DownloadBackupKeys = ({ onGenerate }: Props): JSX.Element => {
return ( return (
<div className="flex flex-col items-center w-full h-full md:px-6 mx-auto mb-36 md:mb-16"> <div className="flex flex-col items-center w-full h-full md:px-6 mx-auto mb-36 md:mb-16">
<p className="text-xl text-center font-medium flex justify-center text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200"> <p className="text-xl text-center font-medium flex flex-col justify-center items-center text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200">
<FontAwesomeIcon icon={faWarning} className="ml-2 mr-3 pt-1 text-2xl text-bunker-200" /> <FontAwesomeIcon icon={faWarning} className="ml-2 mr-3 pt-1 mb-6 text-6xl text-bunker-200" />
{t("signup.step4-message")} {t("signup.step4-message")}
</p> </p>
<div className="flex flex-col pb-2 bg-mineshaft-800 border border-mineshaft-600 items-center justify-center text-center lg:w-1/6 w-full md:min-w-[24rem] mt-8 max-w-md text-bunker-300 text-md rounded-md"> <div className="flex flex-col pb-2 bg-mineshaft-800 border border-mineshaft-600 items-center justify-center text-center lg:w-1/6 w-full md:min-w-[24rem] mt-8 max-w-md text-bunker-300 text-md rounded-md">