Compare commits

...

33 Commits

Author SHA1 Message Date
Maidul Islam
faf6708b00 Merge pull request #1815 from akhilmhdh/fix/migration-mode-patch-v1
feat: maintaince mode enable machine identity login and renew
2024-05-11 11:26:21 -04:00
=
a58d6ebdac feat: maintaince mode enable machine identity login and renew 2024-05-11 20:54:00 +05:30
Sheen Capadngan
6561a9c7be Merge pull request #1804 from Infisical/feat/add-support-for-secret-folder-rename-overview
Feature: add support for secret folder rename in the overview page
2024-05-10 23:07:14 +08:00
Daniel Hougaard
86aaa486b4 Update secret-folder-service.ts 2024-05-10 17:00:30 +02:00
Sheen Capadngan
9880977098 misc: addressed naming suggestion 2024-05-10 22:52:08 +08:00
Sheen Capadngan
b93aaffe77 adjustment: updated to use project slug 2024-05-10 22:34:16 +08:00
Maidul Islam
1ea0d55dd1 Merge pull request #1813 from Infisical/misc/update-documentation-for-github-integration
misc: updated documentation for github integration to include official action
2024-05-10 09:14:14 -04:00
Sheen Capadngan
0866a90c8e misc: updated documentation for github integration 2024-05-10 16:29:12 +08:00
Sheen Capadngan
3fff272cb3 feat: added snapshot for batch 2024-05-10 15:46:31 +08:00
Sheen Capadngan
2559809eac misc: addressed formatting issues 2024-05-10 14:41:35 +08:00
Sheen Capadngan
f32abbdc25 feat: integrate overview folder rename with new batch endpoint 2024-05-10 14:00:49 +08:00
Sheen Capadngan
a6f750fafb feat: added batch update endpoint for folders 2024-05-10 13:57:00 +08:00
Maidul Islam
b4a2123fa3 Merge pull request #1812 from Infisical/delete-pg-migrator
Delete PG migrator folder
2024-05-09 15:19:04 -04:00
Tuan Dang
79cacfa89c Delete PG migrator folder 2024-05-09 12:16:13 -07:00
Maidul Islam
44531487d6 Merge pull request #1811 from Infisical/maidul-pacth233
revert schema name for memberships-unique-constraint
2024-05-09 13:46:32 -04:00
Maidul Islam
7c77a4f049 revert schema name 2024-05-09 13:42:23 -04:00
Maidul Islam
9dfb587032 Merge pull request #1810 from Infisical/check-saml-email-verification
Update isEmailVerified field upon invite signups
2024-05-09 13:03:52 -04:00
Tuan Dang
3952ad9a2e Update isEmailVerified field upon invite signups 2024-05-09 09:51:53 -07:00
Akhil Mohan
9c15cb407d Merge pull request #1806 from Infisical/aws-non-delete
Add option to not delete secrets in parameter store
2024-05-09 21:56:48 +05:30
Maidul Islam
cb17efa10b Merge pull request #1809 from akhilmhdh/fix/patches-v2
Workspace slug support in secret v3 Get Key
2024-05-09 12:17:14 -04:00
Maidul Islam
4adc2c4927 update api descriptions 2024-05-09 12:11:46 -04:00
Maidul Islam
1a26b34ad8 Merge pull request #1805 from Infisical/revise-aws-auth
Reframe AWS IAM auth to AWS Auth with IAM type
2024-05-09 12:06:31 -04:00
=
21c339d27a fix: better error message on ua based login error 2024-05-09 21:32:09 +05:30
=
20f29c752d fix: added workspaceSlug support get secret by key 2024-05-09 21:23:57 +05:30
BlackMagiq
29ea12f8b1 Merge pull request #1807 from Infisical/mermaid-universal-auth
Add mermaid diagram for Universal Auth
2024-05-08 22:05:12 -07:00
Tuan Dang
b4f1cce587 Add mermaid diagram for universal auth 2024-05-08 22:03:57 -07:00
Maidul Islam
5a92520ca3 Update build-staging-and-deploy-aws.yml 2024-05-09 00:53:42 -04:00
Vladyslav Matsiiako
79704e9c98 add option to not delete secrets in parameter store 2024-05-08 21:49:09 -07:00
Maidul Islam
1165d11816 Update build-staging-and-deploy-aws.yml 2024-05-09 00:27:21 -04:00
Maidul Islam
86d4d88b58 package json lock 2024-05-09 00:19:44 -04:00
Maidul Islam
a12ad91e59 Update build-staging-and-deploy-aws.yml 2024-05-09 00:15:42 -04:00
Sheen Capadngan
522dd0836e feat: added validation for folder name duplicates 2024-05-08 23:25:33 +08:00
Sheen Capadngan
e461787c78 feat: added support for renaming folders in the overview page 2024-05-08 23:24:33 +08:00
183 changed files with 449 additions and 13000 deletions

View File

@@ -74,21 +74,21 @@ jobs:
uses: pr-mpt/actions-commit-hash@v2
- name: Download task definition
run: |
aws ecs describe-task-definition --task-definition infisical-prod-platform --query taskDefinition > task-definition.json
aws ecs describe-task-definition --task-definition infisical-core-platform --query taskDefinition > task-definition.json
- name: Render Amazon ECS task definition
id: render-web-container
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: task-definition.json
container-name: infisical-prod-platform
container-name: infisical-core-platform
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
environment-variables: "LOG_LEVEL=info"
- name: Deploy to Amazon ECS service
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
service: infisical-prod-platform
cluster: infisical-prod-platform
service: infisical-core-platform
cluster: infisical-core-platform
wait-for-service-stability: true
production-postgres-deployment:
@@ -135,6 +135,6 @@ jobs:
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
service: infisical-prod-platform
cluster: infisical-prod-platform
service: infisical-core-platform
cluster: infisical-core-platform
wait-for-service-stability: true

View File

@@ -49,7 +49,7 @@
"libsodium-wrappers": "^0.7.13",
"lodash.isequal": "^4.5.0",
"ms": "^2.1.3",
"mysql2": "^3.9.4",
"mysql2": "^3.9.7",
"nanoid": "^5.0.4",
"nodemailer": "^6.9.9",
"ora": "^7.0.1",
@@ -10102,9 +10102,9 @@
}
},
"node_modules/mysql2": {
"version": "3.9.4",
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.9.4.tgz",
"integrity": "sha512-OEESQuwxMza803knC1YSt7NMuc1BrK9j7gZhCSs2WAyxr1vfiI7QLaLOKTh5c9SWGz98qVyQUbK8/WckevNQhg==",
"version": "3.9.7",
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.9.7.tgz",
"integrity": "sha512-KnJT8vYRcNAZv73uf9zpXqNbvBG7DJrs+1nACsjZP1HMJ1TgXEy8wnNilXAn/5i57JizXKtrUtwDB7HxT9DDpw==",
"dependencies": {
"denque": "^2.1.0",
"generate-function": "^2.3.1",

View File

@@ -252,6 +252,7 @@ export const FOLDERS = {
name: "The new name of the folder.",
path: "The path of the folder to update.",
directory: "The new directory of the folder to update. (Deprecated in favor of path)",
projectSlug: "The slug of the project where the folder is located.",
workspaceId: "The ID of the project where the folder is located."
},
DELETE: {
@@ -288,7 +289,8 @@ export const RAW_SECRETS = {
recursive:
"Whether or not to fetch all secrets from the specified base path, and all of its subdirectories. Note, the max depth is 20 deep.",
workspaceId: "The ID of the project to list secrets from.",
workspaceSlug: "The slug of the project to list secrets from. This parameter is only usable by machine identities.",
workspaceSlug:
"The slug of the project to list secrets from. This parameter is only applicable by machine identities.",
environment: "The slug of the environment to list secrets from.",
secretPath: "The secret path to list secrets from.",
includeImports: "Weather to include imported secrets or not."
@@ -307,6 +309,7 @@ export const RAW_SECRETS = {
GET: {
secretName: "The name of the secret to get.",
workspaceId: "The ID of the project to get the secret from.",
workspaceSlug: "The slug of the project to get the secret from.",
environment: "The slug of the environment to get the secret from.",
secretPath: "The path of the secret to get.",
version: "The version of the secret to get.",
@@ -625,7 +628,8 @@ export const INTEGRATION = {
shouldAutoRedeploy: "Used by Render to trigger auto deploy.",
secretGCPLabel: "The label for GCP secrets.",
secretAWSTag: "The tags for AWS secrets.",
kmsKeyId: "The ID of the encryption key from AWS KMS."
kmsKeyId: "The ID of the encryption key from AWS KMS.",
shouldDisableDelete: "The flag to disable deletion of secrets in AWS Parameter Store."
}
},
UPDATE: {

View File

@@ -5,8 +5,13 @@ import { getConfig } from "@app/lib/config/env";
export const maintenanceMode = fp(async (fastify) => {
fastify.addHook("onRequest", async (req) => {
const serverEnvs = getConfig();
if (req.url !== "/api/v1/auth/checkAuth" && req.method !== "GET" && serverEnvs.MAINTENANCE_MODE) {
throw new Error("Infisical is in maintenance mode. Please try again later.");
if (serverEnvs.MAINTENANCE_MODE) {
// skip if its universal auth login or renew
if (req.url === "/api/v1/auth/universal-auth/login" && req.method === "POST") return;
if (req.url === "/api/v1/auth/token/renew" && req.method === "POST") return;
if (req.url !== "/api/v1/auth/checkAuth" && req.method !== "GET") {
throw new Error("Infisical is in maintenance mode. Please try again later.");
}
}
});
});

View File

@@ -538,8 +538,10 @@ export const registerRoutes = async (
folderDAL,
folderVersionDAL,
projectEnvDAL,
snapshotService
snapshotService,
projectDAL
});
const integrationAuthService = integrationAuthServiceFactory({
integrationAuthDAL,
integrationDAL,

View File

@@ -66,7 +66,8 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
)
.optional()
.describe(INTEGRATION.CREATE.metadata.secretAWSTag),
kmsKeyId: z.string().optional().describe(INTEGRATION.CREATE.metadata.kmsKeyId)
kmsKeyId: z.string().optional().describe(INTEGRATION.CREATE.metadata.kmsKeyId),
shouldDisableDelete: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldDisableDelete)
})
.default({})
}),

View File

@@ -127,6 +127,70 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
}
});
server.route({
url: "/batch",
method: "PATCH",
config: {
rateLimit: secretsLimit
},
schema: {
description: "Update folders by batch",
security: [
{
bearerAuth: []
}
],
body: z.object({
projectSlug: z.string().trim().describe(FOLDERS.UPDATE.projectSlug),
folders: z
.object({
id: z.string().describe(FOLDERS.UPDATE.folderId),
environment: z.string().trim().describe(FOLDERS.UPDATE.environment),
name: z.string().trim().describe(FOLDERS.UPDATE.name),
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(FOLDERS.UPDATE.path)
})
.array()
.min(1)
}),
response: {
200: z.object({
folders: SecretFoldersSchema.array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { newFolders, oldFolders, projectId } = await server.services.folder.updateManyFolders({
...req.body,
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await Promise.all(
req.body.folders.map(async (folder, index) => {
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId,
event: {
type: EventType.UPDATE_FOLDER,
metadata: {
environment: oldFolders[index].envId,
folderId: oldFolders[index].id,
folderPath: folder.path,
newFolderName: newFolders[index].name,
oldFolderName: oldFolders[index].name
}
}
});
})
);
return { folders: newFolders };
}
});
// TODO(daniel): Expose this route in api reference and write docs for it.
server.route({
method: "DELETE",

View File

@@ -293,6 +293,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
}),
querystring: z.object({
workspaceId: z.string().trim().optional().describe(RAW_SECRETS.GET.workspaceId),
workspaceSlug: z.string().trim().optional().describe(RAW_SECRETS.GET.workspaceSlug),
environment: z.string().trim().optional().describe(RAW_SECRETS.GET.environment),
secretPath: z.string().trim().default("/").transform(removeTrailingSlash).describe(RAW_SECRETS.GET.secretPath),
version: z.coerce.number().optional().describe(RAW_SECRETS.GET.version),
@@ -311,6 +312,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { workspaceSlug } = req.query;
let { secretPath, environment, workspaceId } = req.query;
if (req.auth.actor === ActorType.SERVICE) {
const scope = ServiceTokenScopes.parse(req.auth.serviceToken.scopes);
@@ -322,7 +324,9 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
}
}
if (!workspaceId || !environment) throw new BadRequestError({ message: "Missing workspace id or environment" });
if (!environment) throw new BadRequestError({ message: "Missing environment" });
if (!workspaceId && !workspaceSlug)
throw new BadRequestError({ message: "You must provide workspaceSlug or workspaceId" });
const secret = await server.services.secret.getSecretByNameRaw({
actorId: req.permission.id,
@@ -331,6 +335,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
actorOrgId: req.permission.orgId,
environment,
projectId: workspaceId,
projectSlug: workspaceSlug,
path: secretPath,
secretName: req.params.secretName,
type: req.query.type,
@@ -339,7 +344,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
});
await server.services.auditLog.createAuditLog({
projectId: req.query.workspaceId,
projectId: secret.workspace,
...req.auditLogInfo,
event: {
type: EventType.GET_SECRET,
@@ -358,7 +363,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
distinctId: getTelemetryDistinctId(req),
properties: {
numberOfSecrets: 1,
workspaceId,
workspaceId: secret.workspace,
environment,
secretPath: req.query.secretPath,
channel: getUserAgentType(req.headers["user-agent"]),

View File

@@ -52,7 +52,7 @@ export const identityUaServiceFactory = ({
}: TIdentityUaServiceFactoryDep) => {
const login = async (clientId: string, clientSecret: string, ip: string) => {
const identityUa = await identityUaDAL.findOne({ clientId });
if (!identityUa) throw new UnauthorizedError();
if (!identityUa) throw new UnauthorizedError({ message: "Invalid credentials" });
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId: identityUa.identityId });
@@ -68,7 +68,7 @@ export const identityUaServiceFactory = ({
const validClientSecretInfo = clientSecrtInfo.find(({ clientSecretHash }) =>
bcrypt.compareSync(clientSecret, clientSecretHash)
);
if (!validClientSecretInfo) throw new UnauthorizedError();
if (!validClientSecretInfo) throw new UnauthorizedError({ message: "Invalid credentials" });
const { clientSecretTTL, clientSecretNumUses, clientSecretNumUsesLimit } = validClientSecretInfo;
if (Number(clientSecretTTL) > 0) {

View File

@@ -517,20 +517,22 @@ const syncSecretsAWSParameterStore = async ({
})
);
// Identify secrets to delete
await Promise.all(
Object.keys(awsParameterStoreSecretsObj).map(async (key) => {
if (!(key in secrets)) {
// case:
// -> delete secret
await ssm
.deleteParameter({
Name: awsParameterStoreSecretsObj[key].Name as string
})
.promise();
}
})
);
if (!metadata.shouldDisableDelete) {
// Identify secrets to delete
await Promise.all(
Object.keys(awsParameterStoreSecretsObj).map(async (key) => {
if (!(key in secrets)) {
// case:
// -> delete secret
await ssm
.deleteParameter({
Name: awsParameterStoreSecretsObj[key].Name as string
})
.promise();
}
})
);
}
};
/**

View File

@@ -27,6 +27,7 @@ export type TCreateIntegrationDTO = {
value: string;
}[];
kmsKeyId?: string;
shouldDisableDelete?: boolean;
};
} & Omit<TProjectPermission, "projectId">;

View File

@@ -546,6 +546,10 @@ export const orgServiceFactory = ({
code
});
await userDAL.updateById(user.id, {
isEmailVerified: true
});
if (user.isAccepted) {
// this means user has already completed signup process
// isAccepted is set true when keys are exchanged

View File

@@ -8,9 +8,16 @@ import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services
import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service";
import { BadRequestError } from "@app/lib/errors";
import { TProjectDALFactory } from "../project/project-dal";
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
import { TSecretFolderDALFactory } from "./secret-folder-dal";
import { TCreateFolderDTO, TDeleteFolderDTO, TGetFolderDTO, TUpdateFolderDTO } from "./secret-folder-types";
import {
TCreateFolderDTO,
TDeleteFolderDTO,
TGetFolderDTO,
TUpdateFolderDTO,
TUpdateManyFoldersDTO
} from "./secret-folder-types";
import { TSecretFolderVersionDALFactory } from "./secret-folder-version-dal";
type TSecretFolderServiceFactoryDep = {
@@ -19,6 +26,7 @@ type TSecretFolderServiceFactoryDep = {
folderDAL: TSecretFolderDALFactory;
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
folderVersionDAL: TSecretFolderVersionDALFactory;
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug">;
};
export type TSecretFolderServiceFactory = ReturnType<typeof secretFolderServiceFactory>;
@@ -28,7 +36,8 @@ export const secretFolderServiceFactory = ({
snapshotService,
permissionService,
projectEnvDAL,
folderVersionDAL
folderVersionDAL,
projectDAL
}: TSecretFolderServiceFactoryDep) => {
const createFolder = async ({
projectId,
@@ -116,6 +125,105 @@ export const secretFolderServiceFactory = ({
return folder;
};
const updateManyFolders = async ({
actor,
actorId,
projectSlug,
actorAuthMethod,
actorOrgId,
folders
}: TUpdateManyFoldersDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) {
throw new BadRequestError({ message: "Project not found" });
}
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
project.id,
actorAuthMethod,
actorOrgId
);
folders.forEach(({ environment, path: secretPath }) => {
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Edit,
subject(ProjectPermissionSub.Secrets, { environment, secretPath })
);
});
const result = await folderDAL.transaction(async (tx) =>
Promise.all(
folders.map(async (newFolder) => {
const { environment, path: secretPath, id, name } = newFolder;
const parentFolder = await folderDAL.findBySecretPath(project.id, environment, secretPath);
if (!parentFolder) {
throw new BadRequestError({ message: "Secret path not found", name: "Batch update folder" });
}
const env = await projectEnvDAL.findOne({ projectId: project.id, slug: environment });
if (!env) {
throw new BadRequestError({ message: "Environment not found", name: "Batch update folder" });
}
const folder = await folderDAL
.findOne({ envId: env.id, id, parentId: parentFolder.id })
// now folder api accepts id based change
// this is for cli backward compatiability and when cli removes this, we will remove this logic
.catch(() => folderDAL.findOne({ envId: env.id, name: id, parentId: parentFolder.id }));
if (!folder) {
throw new BadRequestError({ message: "Folder not found" });
}
if (name !== folder.name) {
// ensure that new folder name is unique
const folderToCheck = await folderDAL.findOne({
name,
envId: env.id,
parentId: parentFolder.id
});
if (folderToCheck) {
throw new BadRequestError({
message: "Folder with specified name already exists",
name: "Batch update folder"
});
}
}
const [doc] = await folderDAL.update(
{ envId: env.id, id: folder.id, parentId: parentFolder.id },
{ name },
tx
);
await folderVersionDAL.create(
{
name: doc.name,
envId: doc.envId,
version: doc.version,
folderId: doc.id
},
tx
);
if (!doc) {
throw new BadRequestError({ message: "Folder not found", name: "Batch update folder" });
}
return { oldFolder: folder, newFolder: doc };
})
)
);
await Promise.all(result.map(async (res) => snapshotService.performSnapshot(res.newFolder.parentId as string)));
return {
projectId: project.id,
newFolders: result.map((res) => res.newFolder),
oldFolders: result.map((res) => res.oldFolder)
};
};
const updateFolder = async ({
projectId,
actor,
@@ -151,6 +259,21 @@ export const secretFolderServiceFactory = ({
.catch(() => folderDAL.findOne({ envId: env.id, name: id, parentId: parentFolder.id }));
if (!folder) throw new BadRequestError({ message: "Folder not found" });
if (name !== folder.name) {
// ensure that new folder name is unique
const folderToCheck = await folderDAL.findOne({
name,
envId: env.id,
parentId: parentFolder.id
});
if (folderToCheck) {
throw new BadRequestError({
message: "Folder with specified name already exists",
name: "Update folder"
});
}
}
const newFolder = await folderDAL.transaction(async (tx) => {
const [doc] = await folderDAL.update({ envId: env.id, id: folder.id, parentId: parentFolder.id }, { name }, tx);
@@ -239,6 +362,7 @@ export const secretFolderServiceFactory = ({
return {
createFolder,
updateFolder,
updateManyFolders,
deleteFolder,
getFolders
};

View File

@@ -13,6 +13,16 @@ export type TUpdateFolderDTO = {
name: string;
} & TProjectPermission;
export type TUpdateManyFoldersDTO = {
projectSlug: string;
folders: {
environment: string;
path: string;
id: string;
name: string;
}[];
} & Omit<TProjectPermission, "projectId">;
export type TDeleteFolderDTO = {
environment: string;
path: string;

View File

@@ -972,7 +972,8 @@ export const secretServiceFactory = ({
path,
actor,
environment,
projectId,
projectId: workspaceId,
projectSlug,
actorId,
actorOrgId,
actorAuthMethod,
@@ -980,6 +981,8 @@ export const secretServiceFactory = ({
includeImports,
version
}: TGetASecretRawDTO) => {
const projectId = workspaceId || (await projectDAL.findProjectBySlug(projectSlug as string, actorOrgId)).id;
const botKey = await projectBotService.getBotKey(projectId);
if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" });

View File

@@ -152,7 +152,9 @@ export type TGetASecretRawDTO = {
type: "shared" | "personal";
includeImports?: boolean;
version?: number;
} & TProjectPermission;
projectSlug?: string;
projectId?: string;
} & Omit<TProjectPermission, "projectId">;
export type TCreateSecretRawDTO = TProjectPermission & {
secretPath: string;

View File

@@ -5,6 +5,25 @@ description: "Learn how to authenticate to Infisical from any platform or enviro
**Universal Auth** is a platform-agnostic authentication method that can be configured for a [machine identity](/documentation/platform/identities/machine-identities) suitable to authenticate from any platform/environment.
## Diagram
The following sequence digram illustrates the Universal Auth workflow for authenticating clients with Infisical.
```mermaid
sequenceDiagram
participant Client as Client
participant Infis as Infisical
Note over Client,Infis: Step 1: Login Operation
Client->>Infis: Send Client ID and Client Secret
Note over Infis: Step 2: Client ID and Client Secret validation
Infis->>Client: Return short-lived access token
Note over Client,Infis: Step 3: Access Infisical API with Token
Client->>Infis: Make authenticated requests using the short-lived access token
```
## Concept
In this method, Infisical authenticates an identity by verifying the credentials issued for it at the `/api/v1/auth/universal-auth/login` endpoint. If successful,
@@ -12,7 +31,7 @@ then Infisical returns a short-lived access token that can be used to make authe
In Universal Auth, an identity is given a **Client ID** and one or more **Client Secret(s)**. Together, a **Client ID** and **Client Secret** can be exchanged for a short-lived access token to authenticate with the Infisical API.
## Workflow
## Guide
In the following steps, we explore how to create and use identities for your workloads and applications to access the Infisical API
using the Universal Auth authentication method.

View File

@@ -3,9 +3,15 @@ title: "GitHub Actions"
description: "How to sync secrets from Infisical to GitHub Actions"
---
<Note>
Alternatively, you can use Infisical's official Github Action
[here](https://github.com/Infisical/secrets-action).
</Note>
Infisical lets you sync secrets to GitHub at the organization-level, repository-level, and repository environment-level.
Prerequisites:
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
- Ensure that you have admin privileges to the repository you want to sync secrets to.

View File

@@ -68,6 +68,7 @@ export const useCreateIntegration = () => {
value: string;
}[];
kmsKeyId?: string;
shouldDisableDelete?: boolean;
};
}) => {
const {

View File

@@ -16,6 +16,7 @@ import {
TGetFoldersByEnvDTO,
TGetProjectFoldersDTO,
TSecretFolder,
TUpdateFolderBatchDTO,
TUpdateFolderDTO
} from "./types";
@@ -190,3 +191,43 @@ export const useDeleteFolder = () => {
}
});
};
export const useUpdateFolderBatch = () => {
const queryClient = useQueryClient();
return useMutation<{}, {}, TUpdateFolderBatchDTO>({
mutationFn: async ({ projectSlug, folders }) => {
const { data } = await apiRequest.patch("/api/v1/folders/batch", {
projectSlug,
folders
});
return data;
},
onSuccess: (_, { projectId, folders }) => {
folders.forEach((folder) => {
queryClient.invalidateQueries(
folderQueryKeys.getSecretFolders({
projectId,
environment: folder.environment,
path: folder.path
})
);
queryClient.invalidateQueries(
secretSnapshotKeys.list({
workspaceId: projectId,
environment: folder.environment,
directory: folder.path
})
);
queryClient.invalidateQueries(
secretSnapshotKeys.count({
workspaceId: projectId,
environment: folder.environment,
directory: folder.path
})
);
});
}
});
};

View File

@@ -36,3 +36,14 @@ export type TDeleteFolderDTO = {
folderId: string;
path?: string;
};
export type TUpdateFolderBatchDTO = {
projectId: string;
projectSlug: string;
folders: {
name: string;
environment: string;
id: string;
path?: string;
}[];
};

View File

@@ -89,6 +89,7 @@ export default function AWSParameterStoreCreateIntegrationPage() {
const [isLoading, setIsLoading] = useState(false);
const [shouldTag, setShouldTag] = useState(false);
const [shouldDisableDelete, setShouldDisableDelete] = useState(false);
const [tagKey, setTagKey] = useState("");
const [tagValue, setTagValue] = useState("");
const [kmsKeyId, setKmsKeyId] = useState("");
@@ -144,7 +145,8 @@ export default function AWSParameterStoreCreateIntegrationPage() {
]
}
: {}),
...(kmsKeyId && { kmsKeyId })
...(kmsKeyId && { kmsKeyId }),
...(shouldDisableDelete && { shouldDisableDelete })
}
});
@@ -273,6 +275,15 @@ export default function AWSParameterStoreCreateIntegrationPage() {
exit={{ opacity: 0, translateX: 30 }}
>
<div className="mt-2 ml-1">
<Switch
id="delete-aws"
onCheckedChange={() => setShouldDisableDelete(!shouldDisableDelete)}
isChecked={shouldDisableDelete}
>
Disable deleting secrets in AWS Parameter Store
</Switch>
</div>
<div className="mt-4 ml-1">
<Switch
id="tag-aws"
onCheckedChange={() => setShouldTag(!shouldTag)}

View File

@@ -47,6 +47,7 @@ import {
ProjectPermissionActions,
ProjectPermissionSub,
useOrganization,
useProjectPermission,
useWorkspace
} from "@app/context";
import { usePopUp } from "@app/hooks";
@@ -61,6 +62,9 @@ import {
useGetUserWsKey,
useUpdateSecretV3
} from "@app/hooks/api";
import { useUpdateFolderBatch } from "@app/hooks/api/secretFolders/queries";
import { TUpdateFolderBatchDTO } from "@app/hooks/api/secretFolders/types";
import { TSecretFolder } from "@app/hooks/api/types";
import { ProjectVersion } from "@app/hooks/api/workspace/types";
import { FolderForm } from "../SecretMainPage/components/ActionBar/FolderForm";
@@ -87,6 +91,7 @@ export const SecretOverviewPage = () => {
const parentTableRef = useRef<HTMLTableElement>(null);
const [expandableTableWidth, setExpandableTableWidth] = useState(0);
const [sortDir, setSortDir] = useState<"asc" | "desc">("asc");
const { permission } = useProjectPermission();
useEffect(() => {
if (parentTableRef.current) {
@@ -201,11 +206,13 @@ export const SecretOverviewPage = () => {
const { mutateAsync: updateSecretV3 } = useUpdateSecretV3();
const { mutateAsync: deleteSecretV3 } = useDeleteSecretV3();
const { mutateAsync: createFolder } = useCreateFolder();
const { mutateAsync: updateFolderBatch } = useUpdateFolderBatch();
const { handlePopUpOpen, handlePopUpToggle, handlePopUpClose, popUp } = usePopUp([
"addSecretsInAllEnvs",
"addFolder",
"misc"
"misc",
"updateFolder"
] as const);
const handleFolderCreate = async (folderName: string) => {
@@ -236,6 +243,59 @@ export const SecretOverviewPage = () => {
}
};
const handleFolderUpdate = async (newFolderName: string) => {
const { name: oldFolderName } = popUp.updateFolder.data as TSecretFolder;
const updatedFolders: TUpdateFolderBatchDTO["folders"] = [];
userAvailableEnvs.forEach((env) => {
if (
permission.can(
ProjectPermissionActions.Edit,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath })
)
) {
const folder = getFolderByNameAndEnv(oldFolderName, env.slug);
if (folder) {
updatedFolders.push({
environment: env.slug,
name: newFolderName,
id: folder.id,
path: secretPath
});
}
}
});
if (updatedFolders.length === 0) {
createNotification({
type: "info",
text: "You don't have access to rename selected folder"
});
handlePopUpClose("updateFolder");
return;
}
try {
await updateFolderBatch({
projectSlug,
folders: updatedFolders,
projectId: workspaceId
});
createNotification({
type: "success",
text: "Successfully renamed folder across environments"
});
} catch (err) {
createNotification({
type: "error",
text: "Failed to rename folder across environments"
});
} finally {
handlePopUpClose("updateFolder");
}
};
const handleSecretCreate = async (env: string, key: string, value: string) => {
try {
// create folder if not existing
@@ -726,6 +786,9 @@ export const SecretOverviewPage = () => {
environments={visibleEnvs}
key={`overview-${folderName}-${index + 1}`}
onClick={handleFolderClick}
onToggleFolderEdit={(name: string) =>
handlePopUpOpen("updateFolder", { name })
}
/>
))}
{!isTableLoading &&
@@ -800,6 +863,18 @@ export const SecretOverviewPage = () => {
<FolderForm onCreateFolder={handleFolderCreate} />
</ModalContent>
</Modal>
<Modal
isOpen={popUp.updateFolder.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("updateFolder", isOpen)}
>
<ModalContent title="Edit Folder Name">
<FolderForm
isEdit
defaultFolderName={(popUp.updateFolder?.data as Pick<TSecretFolder, "name">)?.name}
onUpdateFolder={handleFolderUpdate}
/>
</ModalContent>
</Modal>
</>
);
};

View File

@@ -1,8 +1,8 @@
import { faCheck, faFolder, faXmark } from "@fortawesome/free-solid-svg-icons";
import { faCheck, faFolder, faPencil, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { twMerge } from "tailwind-merge";
import { Checkbox, Td, Tr } from "@app/components/v2";
import { Checkbox, IconButton, Td, Tr } from "@app/components/v2";
type Props = {
folderName: string;
@@ -11,6 +11,7 @@ type Props = {
onClick: (path: string) => void;
isSelected: boolean;
onToggleFolderSelect: (folderName: string) => void;
onToggleFolderEdit: (name: string) => void;
};
export const SecretOverviewFolderRow = ({
@@ -19,6 +20,7 @@ export const SecretOverviewFolderRow = ({
isFolderPresentInEnv,
isSelected,
onToggleFolderSelect,
onToggleFolderEdit,
onClick
}: Props) => {
return (
@@ -43,6 +45,18 @@ export const SecretOverviewFolderRow = ({
/>
</div>
<div>{folderName}</div>
<IconButton
ariaLabel="edit-folder"
variant="plain"
size="sm"
className="p-0 opacity-0 group-hover:opacity-100"
onClick={(e) => {
onToggleFolderEdit(folderName);
e.stopPropagation();
}}
>
<FontAwesomeIcon icon={faPencil} size="sm" />
</IconButton>
</div>
</Td>
{environments.map(({ slug }, i) => {

View File

@@ -1 +0,0 @@
db

File diff suppressed because it is too large Load Diff

View File

@@ -1,33 +0,0 @@
{
"name": "pg-migrator",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"migration": "tsx src/index.ts",
"rollback": "tsx src/rollback.ts",
"migrate:audit-log": "tsx src/audit-log-migrator.ts"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@types/node": "^20.11.0",
"@types/prompt-sync": "^4.2.3",
"@types/uuid": "^9.0.7",
"tsx": "^4.7.0",
"typescript": "^5.3.3"
},
"dependencies": {
"@casl/ability": "^6.5.0",
"@sindresorhus/slugify": "^2.2.1",
"dotenv": "^16.3.1",
"knex": "^3.1.0",
"level": "^8.0.0",
"mongoose": "^8.0.4",
"nanoid": "^5.0.4",
"pg": "^8.11.3",
"prompt-sync": "^4.2.0",
"uuid": "^9.0.1",
"zod": "^3.22.4"
}
}

View File

@@ -1,451 +0,0 @@
import { Knex } from "knex";
import {
TableName,
TApiKeys,
TApiKeysInsert,
TApiKeysUpdate,
TAuditLogs,
TAuditLogsInsert,
TAuditLogsUpdate,
TAuthTokens,
TAuthTokenSessions,
TAuthTokenSessionsInsert,
TAuthTokenSessionsUpdate,
TAuthTokensInsert,
TAuthTokensUpdate,
TBackupPrivateKey,
TBackupPrivateKeyInsert,
TBackupPrivateKeyUpdate,
TGitAppInstallSessions,
TGitAppInstallSessionsInsert,
TGitAppInstallSessionsUpdate,
TGitAppOrg,
TGitAppOrgInsert,
TGitAppOrgUpdate,
TIdentities,
TIdentitiesInsert,
TIdentitiesUpdate,
TIdentityAccessTokens,
TIdentityAccessTokensInsert,
TIdentityAccessTokensUpdate,
TIdentityOrgMemberships,
TIdentityOrgMembershipsInsert,
TIdentityOrgMembershipsUpdate,
TIdentityProjectMemberships,
TIdentityProjectMembershipsInsert,
TIdentityProjectMembershipsUpdate,
TIdentityUaClientSecrets,
TIdentityUaClientSecretsInsert,
TIdentityUaClientSecretsUpdate,
TIdentityUniversalAuths,
TIdentityUniversalAuthsInsert,
TIdentityUniversalAuthsUpdate,
TIncidentContacts,
TIncidentContactsInsert,
TIncidentContactsUpdate,
TIntegrationAuths,
TIntegrationAuthsInsert,
TIntegrationAuthsUpdate,
TIntegrations,
TIntegrationsInsert,
TIntegrationsUpdate,
TOrganizations,
TOrganizationsInsert,
TOrganizationsUpdate,
TOrgBots,
TOrgBotsInsert,
TOrgBotsUpdate,
TOrgMemberships,
TOrgMembershipsInsert,
TOrgMembershipsUpdate,
TOrgRoles,
TOrgRolesInsert,
TOrgRolesUpdate,
TProjectBots,
TProjectBotsInsert,
TProjectBotsUpdate,
TProjectEnvironments,
TProjectEnvironmentsInsert,
TProjectEnvironmentsUpdate,
TProjectKeys,
TProjectKeysInsert,
TProjectKeysUpdate,
TProjectMemberships,
TProjectMembershipsInsert,
TProjectMembershipsUpdate,
TProjectRoles,
TProjectRolesInsert,
TProjectRolesUpdate,
TProjects,
TProjectsInsert,
TProjectsUpdate,
TSamlConfigs,
TSamlConfigsInsert,
TSamlConfigsUpdate,
TSecretApprovalPolicies,
TSecretApprovalPoliciesApprovers,
TSecretApprovalPoliciesApproversInsert,
TSecretApprovalPoliciesApproversUpdate,
TSecretApprovalPoliciesInsert,
TSecretApprovalPoliciesUpdate,
TSecretApprovalRequests,
TSecretApprovalRequestSecretTags,
TSecretApprovalRequestSecretTagsInsert,
TSecretApprovalRequestSecretTagsUpdate,
TSecretApprovalRequestsInsert,
TSecretApprovalRequestsReviewers,
TSecretApprovalRequestsReviewersInsert,
TSecretApprovalRequestsReviewersUpdate,
TSecretApprovalRequestsSecrets,
TSecretApprovalRequestsSecretsInsert,
TSecretApprovalRequestsSecretsUpdate,
TSecretApprovalRequestsUpdate,
TSecretBlindIndexes,
TSecretBlindIndexesInsert,
TSecretBlindIndexesUpdate,
TSecretFolders,
TSecretFoldersInsert,
TSecretFoldersUpdate,
TSecretFolderVersions,
TSecretFolderVersionsInsert,
TSecretFolderVersionsUpdate,
TSecretImports,
TSecretImportsInsert,
TSecretImportsUpdate,
TSecretRotationOutputs,
TSecretRotationOutputsInsert,
TSecretRotationOutputsUpdate,
TSecretRotations,
TSecretRotationsInsert,
TSecretRotationsUpdate,
TSecrets,
TSecretScanningGitRisks,
TSecretScanningGitRisksInsert,
TSecretScanningGitRisksUpdate,
TSecretsInsert,
TSecretSnapshotFolders,
TSecretSnapshotFoldersInsert,
TSecretSnapshotFoldersUpdate,
TSecretSnapshots,
TSecretSnapshotSecrets,
TSecretSnapshotSecretsInsert,
TSecretSnapshotSecretsUpdate,
TSecretSnapshotsInsert,
TSecretSnapshotsUpdate,
TSecretsUpdate,
TSecretTagJunction,
TSecretTagJunctionInsert,
TSecretTagJunctionUpdate,
TSecretTags,
TSecretTagsInsert,
TSecretTagsUpdate,
TSecretVersions,
TSecretVersionsInsert,
TSecretVersionsUpdate,
TSecretVersionTagJunction,
TSecretVersionTagJunctionInsert,
TSecretVersionTagJunctionUpdate,
TServiceTokens,
TServiceTokensInsert,
TServiceTokensUpdate,
TSuperAdmin,
TSuperAdminInsert,
TSuperAdminUpdate,
TTrustedIps,
TTrustedIpsInsert,
TTrustedIpsUpdate,
TUserActions,
TUserActionsInsert,
TUserActionsUpdate,
TUserEncryptionKeys,
TUserEncryptionKeysInsert,
TUserEncryptionKeysUpdate,
TUsers,
TUsersInsert,
TUsersUpdate,
TWebhooks,
TWebhooksInsert,
TWebhooksUpdate,
} from "../schemas";
declare module "knex/types/tables" {
interface Tables {
[TableName.Users]: Knex.CompositeTableType<
TUsers,
TUsersInsert,
TUsersUpdate
>;
[TableName.UserEncryptionKey]: Knex.CompositeTableType<
TUserEncryptionKeys,
TUserEncryptionKeysInsert,
TUserEncryptionKeysUpdate
>;
[TableName.AuthTokens]: Knex.CompositeTableType<
TAuthTokens,
TAuthTokensInsert,
TAuthTokensUpdate
>;
[TableName.AuthTokenSession]: Knex.CompositeTableType<
TAuthTokenSessions,
TAuthTokenSessionsInsert,
TAuthTokenSessionsUpdate
>;
[TableName.BackupPrivateKey]: Knex.CompositeTableType<
TBackupPrivateKey,
TBackupPrivateKeyInsert,
TBackupPrivateKeyUpdate
>;
[TableName.Organization]: Knex.CompositeTableType<
TOrganizations,
TOrganizationsInsert,
TOrganizationsUpdate
>;
[TableName.OrgMembership]: Knex.CompositeTableType<
TOrgMemberships,
TOrgMembershipsInsert,
TOrgMembershipsUpdate
>;
[TableName.OrgRoles]: Knex.CompositeTableType<
TOrgRoles,
TOrgRolesInsert,
TOrgRolesUpdate
>;
[TableName.IncidentContact]: Knex.CompositeTableType<
TIncidentContacts,
TIncidentContactsInsert,
TIncidentContactsUpdate
>;
[TableName.UserAction]: Knex.CompositeTableType<
TUserActions,
TUserActionsInsert,
TUserActionsUpdate
>;
[TableName.SuperAdmin]: Knex.CompositeTableType<
TSuperAdmin,
TSuperAdminInsert,
TSuperAdminUpdate
>;
[TableName.ApiKey]: Knex.CompositeTableType<
TApiKeys,
TApiKeysInsert,
TApiKeysUpdate
>;
[TableName.Project]: Knex.CompositeTableType<
TProjects,
TProjectsInsert,
TProjectsUpdate
>;
[TableName.ProjectMembership]: Knex.CompositeTableType<
TProjectMemberships,
TProjectMembershipsInsert,
TProjectMembershipsUpdate
>;
[TableName.Environment]: Knex.CompositeTableType<
TProjectEnvironments,
TProjectEnvironmentsInsert,
TProjectEnvironmentsUpdate
>;
[TableName.ProjectBot]: Knex.CompositeTableType<
TProjectBots,
TProjectBotsInsert,
TProjectBotsUpdate
>;
[TableName.ProjectRoles]: Knex.CompositeTableType<
TProjectRoles,
TProjectRolesInsert,
TProjectRolesUpdate
>;
[TableName.ProjectKeys]: Knex.CompositeTableType<
TProjectKeys,
TProjectKeysInsert,
TProjectKeysUpdate
>;
[TableName.Secret]: Knex.CompositeTableType<
TSecrets,
TSecretsInsert,
TSecretsUpdate
>;
[TableName.SecretBlindIndex]: Knex.CompositeTableType<
TSecretBlindIndexes,
TSecretBlindIndexesInsert,
TSecretBlindIndexesUpdate
>;
[TableName.SecretVersion]: Knex.CompositeTableType<
TSecretVersions,
TSecretVersionsInsert,
TSecretVersionsUpdate
>;
[TableName.SecretFolder]: Knex.CompositeTableType<
TSecretFolders,
TSecretFoldersInsert,
TSecretFoldersUpdate
>;
[TableName.SecretFolderVersion]: Knex.CompositeTableType<
TSecretFolderVersions,
TSecretFolderVersionsInsert,
TSecretFolderVersionsUpdate
>;
[TableName.SecretTag]: Knex.CompositeTableType<
TSecretTags,
TSecretTagsInsert,
TSecretTagsUpdate
>;
[TableName.SecretImport]: Knex.CompositeTableType<
TSecretImports,
TSecretImportsInsert,
TSecretImportsUpdate
>;
[TableName.Integration]: Knex.CompositeTableType<
TIntegrations,
TIntegrationsInsert,
TIntegrationsUpdate
>;
[TableName.Webhook]: Knex.CompositeTableType<
TWebhooks,
TWebhooksInsert,
TWebhooksUpdate
>;
[TableName.ServiceToken]: Knex.CompositeTableType<
TServiceTokens,
TServiceTokensInsert,
TServiceTokensUpdate
>;
[TableName.IntegrationAuth]: Knex.CompositeTableType<
TIntegrationAuths,
TIntegrationAuthsInsert,
TIntegrationAuthsUpdate
>;
[TableName.Identity]: Knex.CompositeTableType<
TIdentities,
TIdentitiesInsert,
TIdentitiesUpdate
>;
[TableName.IdentityUniversalAuth]: Knex.CompositeTableType<
TIdentityUniversalAuths,
TIdentityUniversalAuthsInsert,
TIdentityUniversalAuthsUpdate
>;
[TableName.IdentityUaClientSecret]: Knex.CompositeTableType<
TIdentityUaClientSecrets,
TIdentityUaClientSecretsInsert,
TIdentityUaClientSecretsUpdate
>;
[TableName.IdentityAccessToken]: Knex.CompositeTableType<
TIdentityAccessTokens,
TIdentityAccessTokensInsert,
TIdentityAccessTokensUpdate
>;
[TableName.IdentityOrgMembership]: Knex.CompositeTableType<
TIdentityOrgMemberships,
TIdentityOrgMembershipsInsert,
TIdentityOrgMembershipsUpdate
>;
[TableName.IdentityProjectMembership]: Knex.CompositeTableType<
TIdentityProjectMemberships,
TIdentityProjectMembershipsInsert,
TIdentityProjectMembershipsUpdate
>;
[TableName.SecretApprovalPolicy]: Knex.CompositeTableType<
TSecretApprovalPolicies,
TSecretApprovalPoliciesInsert,
TSecretApprovalPoliciesUpdate
>;
[TableName.SecretApprovalPolicyApprover]: Knex.CompositeTableType<
TSecretApprovalPoliciesApprovers,
TSecretApprovalPoliciesApproversInsert,
TSecretApprovalPoliciesApproversUpdate
>;
[TableName.SecretApprovalRequest]: Knex.CompositeTableType<
TSecretApprovalRequests,
TSecretApprovalRequestsInsert,
TSecretApprovalRequestsUpdate
>;
[TableName.SecretApprovalRequestReviewer]: Knex.CompositeTableType<
TSecretApprovalRequestsReviewers,
TSecretApprovalRequestsReviewersInsert,
TSecretApprovalRequestsReviewersUpdate
>;
[TableName.SecretApprovalRequestSecret]: Knex.CompositeTableType<
TSecretApprovalRequestsSecrets,
TSecretApprovalRequestsSecretsInsert,
TSecretApprovalRequestsSecretsUpdate
>;
[TableName.SecretApprovalRequestSecretTag]: Knex.CompositeTableType<
TSecretApprovalRequestSecretTags,
TSecretApprovalRequestSecretTagsInsert,
TSecretApprovalRequestSecretTagsUpdate
>;
[TableName.SecretRotation]: Knex.CompositeTableType<
TSecretRotations,
TSecretRotationsInsert,
TSecretRotationsUpdate
>;
[TableName.SecretRotationOutput]: Knex.CompositeTableType<
TSecretRotationOutputs,
TSecretRotationOutputsInsert,
TSecretRotationOutputsUpdate
>;
[TableName.Snapshot]: Knex.CompositeTableType<
TSecretSnapshots,
TSecretSnapshotsInsert,
TSecretSnapshotsUpdate
>;
[TableName.SnapshotSecret]: Knex.CompositeTableType<
TSecretSnapshotSecrets,
TSecretSnapshotSecretsInsert,
TSecretSnapshotSecretsUpdate
>;
[TableName.SnapshotFolder]: Knex.CompositeTableType<
TSecretSnapshotFolders,
TSecretSnapshotFoldersInsert,
TSecretSnapshotFoldersUpdate
>;
[TableName.SamlConfig]: Knex.CompositeTableType<
TSamlConfigs,
TSamlConfigsInsert,
TSamlConfigsUpdate
>;
[TableName.OrgBot]: Knex.CompositeTableType<
TOrgBots,
TOrgBotsInsert,
TOrgBotsUpdate
>;
[TableName.AuditLog]: Knex.CompositeTableType<
TAuditLogs,
TAuditLogsInsert,
TAuditLogsUpdate
>;
[TableName.GitAppInstallSession]: Knex.CompositeTableType<
TGitAppInstallSessions,
TGitAppInstallSessionsInsert,
TGitAppInstallSessionsUpdate
>;
[TableName.GitAppOrg]: Knex.CompositeTableType<
TGitAppOrg,
TGitAppOrgInsert,
TGitAppOrgUpdate
>;
[TableName.SecretScanningGitRisk]: Knex.CompositeTableType<
TSecretScanningGitRisks,
TSecretScanningGitRisksInsert,
TSecretScanningGitRisksUpdate
>;
[TableName.TrustedIps]: Knex.CompositeTableType<
TTrustedIps,
TTrustedIpsInsert,
TTrustedIpsUpdate
>;
// Junction tables
[TableName.JnSecretTag]: Knex.CompositeTableType<
TSecretTagJunction,
TSecretTagJunctionInsert,
TSecretTagJunctionUpdate
>;
[TableName.SecretVersionTag]: Knex.CompositeTableType<
TSecretVersionTagJunction,
TSecretVersionTagJunctionInsert,
TSecretVersionTagJunctionUpdate
>;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,36 +0,0 @@
import { TFolderSchema } from "./models";
export const folderBfsTraversal = async (
root: TFolderSchema,
callback: (
data: TFolderSchema & { parentId: string | null },
) => void | Promise<void>,
) => {
const queue = [root];
while (queue.length) {
const folder = queue.pop() as TFolderSchema & { parentId: null };
callback(folder);
queue.push(
...folder.children.map((el) => ({
...el,
parentId: folder.id,
})),
);
}
};
export const flattenFolders = (folders: TFolderSchema) => {
const flattened: {
id: string;
parentId: string | null;
name: string;
version: number;
}[] = [];
if(!folders) return []
folderBfsTraversal(folders, ({ name, version, parentId, id }) => {
flattened.push({ name, version, parentId, id });
});
return flattened;
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,37 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import {
createOnUpdateTrigger,
createUpdateAtTriggerFunction,
dropOnUpdateTrigger,
dropUpdatedAtTriggerFunction
} from "../utils";
export async function up(knex: Knex): Promise<void> {
const isTablePresent = await knex.schema.hasTable(TableName.Users);
if (!isTablePresent) {
await knex.schema.createTable(TableName.Users, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("email").notNullable();
t.specificType("authMethods", "text[]");
t.boolean("superAdmin").defaultTo(false);
t.string("firstName");
t.string("lastName");
t.boolean("isAccepted").defaultTo(false);
t.boolean("isMfaEnabled").defaultTo(false);
t.specificType("mfaMethods", "text[]");
t.jsonb("devices");
t.timestamps(true, true, true);
});
}
// this is a one time function
await createUpdateAtTriggerFunction(knex);
await createOnUpdateTrigger(knex, TableName.Users);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.Users);
await dropOnUpdateTrigger(knex, TableName.Users);
await dropUpdatedAtTriggerFunction(knex);
}

View File

@@ -1,31 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const isTablePresent = await knex.schema.hasTable(TableName.UserEncryptionKey);
if (!isTablePresent) {
await knex.schema.createTable(TableName.UserEncryptionKey, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.text("clientPublicKey");
t.text("serverPrivateKey");
t.integer("encryptionVersion").defaultTo(2);
t.text("protectedKey");
t.text("protectedKeyIV");
t.text("protectedKeyTag");
t.text("publicKey").notNullable();
t.text("encryptedPrivateKey").notNullable();
t.text("iv").notNullable();
t.text("tag").notNullable();
t.text("salt").notNullable();
t.text("verifier").notNullable();
// one to one relationship
t.uuid("userId").notNullable().unique();
t.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE");
});
}
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.UserEncryptionKey);
}

View File

@@ -1,25 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const isTablePresent = await knex.schema.hasTable(TableName.AuthTokens);
if (!isTablePresent) {
await knex.schema.createTable(TableName.AuthTokens, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("type").notNullable();
t.string("phoneNumber");
t.string("tokenHash").notNullable();
t.integer("triesLeft");
t.datetime("expiresAt").notNullable();
// does not need update trigger we will do it manually
t.timestamps(true, true, true);
t.uuid("userId");
t.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE");
});
}
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.AuthTokens);
}

View File

@@ -1,29 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
const isTablePresent = await knex.schema.hasTable(TableName.AuthTokenSession);
if (!isTablePresent) {
await knex.schema.createTable(TableName.AuthTokenSession, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("ip").notNullable();
t.string("userAgent");
t.integer("refreshVersion").notNullable().defaultTo(1);
t.integer("accessVersion").notNullable().defaultTo(1);
t.datetime("lastUsed").notNullable();
// does not need update trigger we will do it manually
t.timestamps(true, true, true);
t.uuid("userId").notNullable();
t.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE");
});
}
// this is a one time function
await createOnUpdateTrigger(knex, TableName.AuthTokenSession);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.AuthTokenSession);
await dropOnUpdateTrigger(knex, TableName.AuthTokenSession);
}

View File

@@ -1,26 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const doesTableExist = await knex.schema.hasTable(TableName.BackupPrivateKey);
if (!doesTableExist) {
await knex.schema.createTable(TableName.BackupPrivateKey, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.text("encryptedPrivateKey").notNullable();
t.text("iv").notNullable();
t.text("tag").notNullable();
t.string("algorithm").notNullable();
t.string("keyEncoding").notNullable();
t.text("salt").notNullable();
t.text("verifier").notNullable();
t.timestamps(true, true, true);
t.uuid("userId").notNullable().unique();
t.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE");
});
}
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.BackupPrivateKey);
}

View File

@@ -1,35 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
const isTablePresent = await knex.schema.hasTable(TableName.Organization);
if (!isTablePresent) {
await knex.schema.createTable(TableName.Organization, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("name").notNullable();
t.string("customerId");
t.string("slug").notNullable();
// does not need update trigger we will do it manually
t.unique("slug");
t.timestamps(true, true, true);
});
await knex.schema.alterTable(TableName.AuthTokens, (t) => {
t.uuid("orgId");
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
});
}
// this is a one time function
await createOnUpdateTrigger(knex, TableName.Organization);
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasColumn(TableName.AuthTokens, "orgId")) {
await knex.schema.alterTable(TableName.AuthTokens, (t) => {
t.dropColumn("orgId");
});
}
await knex.schema.dropTableIfExists(TableName.Organization);
await dropOnUpdateTrigger(knex, TableName.Organization);
}

View File

@@ -1,48 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { OrgMembershipStatus } from "../schemas/models";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
const isOrgRolePresent = await knex.schema.hasTable(TableName.OrgRoles);
if (!isOrgRolePresent) {
await knex.schema.createTable(TableName.OrgRoles, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("name").notNullable();
t.string("description");
t.string("slug").notNullable();
t.jsonb("permissions").notNullable();
// does not need update trigger we will do it manually
t.timestamps(true, true, true);
t.uuid("orgId").notNullable();
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
});
}
const isOrgTablePresent = await knex.schema.hasTable(TableName.OrgMembership);
if (!isOrgTablePresent) {
await knex.schema.createTable(TableName.OrgMembership, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("role").notNullable();
t.string("status").notNullable().defaultTo(OrgMembershipStatus.Invited);
t.string("inviteEmail");
// does not need update trigger we will do it manually
t.timestamps(true, true, true);
t.uuid("userId");
t.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE");
t.uuid("orgId").notNullable();
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
t.uuid("roleId");
t.foreign("roleId").references("id").inTable(TableName.OrgRoles);
});
}
// this is a one time function
await createOnUpdateTrigger(knex, TableName.OrgMembership);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.OrgMembership);
await knex.schema.dropTableIfExists(TableName.OrgRoles);
await dropOnUpdateTrigger(knex, TableName.OrgMembership);
}

View File

@@ -1,25 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
const isTablePresent = await knex.schema.hasTable(TableName.IncidentContact);
if (!isTablePresent) {
await knex.schema.createTable(TableName.IncidentContact, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("email").notNullable();
// does not need update trigger we will do it manually
t.timestamps(true, true, true);
t.uuid("orgId").notNullable();
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
});
}
// this is a one time function
await createOnUpdateTrigger(knex, TableName.IncidentContact);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.IncidentContact);
await dropOnUpdateTrigger(knex, TableName.IncidentContact);
}

View File

@@ -1,20 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const isTablePresent = await knex.schema.hasTable(TableName.UserAction);
if (!isTablePresent) {
await knex.schema.createTable(TableName.UserAction, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("action").notNullable();
t.timestamps(true, true, true);
t.uuid("userId").notNullable();
t.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE");
});
}
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.UserAction);
}

View File

@@ -1,23 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
const isTablePresent = await knex.schema.hasTable(TableName.SuperAdmin);
if (!isTablePresent) {
await knex.schema.createTable(TableName.SuperAdmin, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.boolean("initialized").defaultTo(false);
t.boolean("allowSignUp").defaultTo(true);
t.timestamps(true, true, true);
});
}
// this is a one time function
await createOnUpdateTrigger(knex, TableName.SuperAdmin);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.SuperAdmin);
await dropOnUpdateTrigger(knex, TableName.SuperAdmin);
}

View File

@@ -1,26 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
const isTablePresent = await knex.schema.hasTable(TableName.ApiKey);
if (!isTablePresent) {
await knex.schema.createTable(TableName.ApiKey, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("name").notNullable();
t.datetime("lastUsed");
t.datetime("expiresAt");
t.string("secretHash").notNullable();
t.timestamps(true, true, true);
t.uuid("userId").notNullable();
t.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE");
});
}
await createOnUpdateTrigger(knex, TableName.ApiKey);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.ApiKey);
await dropOnUpdateTrigger(knex, TableName.ApiKey);
}

View File

@@ -1,62 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.Project))) {
await knex.schema.createTable(TableName.Project, (t) => {
t.string("id", 36).primary().defaultTo(knex.fn.uuid());
t.string("name").notNullable();
t.string("slug").notNullable();
t.boolean("autoCapitalization").defaultTo(true);
t.uuid("orgId").notNullable();
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
t.timestamps(true, true, true);
t.unique(["orgId", "slug"]);
});
}
await createOnUpdateTrigger(knex, TableName.Project);
// environments
if (!(await knex.schema.hasTable(TableName.Environment))) {
await knex.schema.createTable(TableName.Environment, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("name").notNullable();
t.string("slug").notNullable();
t.integer("position").notNullable();
t.string("projectId").notNullable();
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
// this will ensure ever env has its position
t.unique(["projectId", "position"], {
indexName: "env_pos_composite_uniqe",
deferrable: "deferred"
});
t.timestamps(true, true, true);
});
}
// project key
if (!(await knex.schema.hasTable(TableName.ProjectKeys))) {
await knex.schema.createTable(TableName.ProjectKeys, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.text("encryptedKey").notNullable();
t.text("nonce").notNullable();
t.uuid("receiverId").notNullable();
t.foreign("receiverId").references("id").inTable(TableName.Users).onDelete("CASCADE");
t.uuid("senderId");
// if sender is deleted just don't do anything to this record
t.foreign("senderId").references("id").inTable(TableName.Users).onDelete("SET NULL");
t.string("projectId").notNullable();
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.ProjectKeys);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.Environment);
await knex.schema.dropTableIfExists(TableName.ProjectKeys);
await knex.schema.dropTableIfExists(TableName.Project);
await dropOnUpdateTrigger(knex, TableName.ProjectKeys);
await dropOnUpdateTrigger(knex, TableName.Project);
}

View File

@@ -1,43 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.ProjectRoles))) {
await knex.schema.createTable(TableName.ProjectRoles, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("name").notNullable();
t.string("description");
t.string("slug").notNullable();
t.jsonb("permissions").notNullable();
// does not need update trigger we will do it manually
t.timestamps(true, true, true);
t.string("projectId").notNullable();
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
});
}
if (!(await knex.schema.hasTable(TableName.ProjectMembership))) {
await knex.schema.createTable(TableName.ProjectMembership, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("role").notNullable();
// does not need update trigger we will do it manually
t.timestamps(true, true, true);
t.uuid("userId").notNullable();
t.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE");
t.string("projectId").notNullable();
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
// until role is changed/removed the role should not deleted
t.uuid("roleId");
t.foreign("roleId").references("id").inTable(TableName.ProjectRoles);
});
}
await createOnUpdateTrigger(knex, TableName.ProjectMembership);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.ProjectMembership);
await knex.schema.dropTableIfExists(TableName.ProjectRoles);
await dropOnUpdateTrigger(knex, TableName.ProjectMembership);
}

View File

@@ -1,42 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.SecretFolder))) {
await knex.schema.createTable(TableName.SecretFolder, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("name").notNullable();
t.integer("version").defaultTo(1);
t.timestamps(true, true, true);
t.uuid("envId").notNullable();
t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE");
t.uuid("parentId");
t.foreign("parentId").references("id").inTable(TableName.SecretFolder).onDelete("CASCADE");
});
}
await createOnUpdateTrigger(knex, TableName.SecretFolder);
if (!(await knex.schema.hasTable(TableName.SecretFolderVersion))) {
await knex.schema.createTable(TableName.SecretFolderVersion, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("name").notNullable();
t.integer("version").defaultTo(1);
t.timestamps(true, true, true);
t.uuid("envId").notNullable();
t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE");
t.uuid("folderId").notNullable();
// t.foreign("folderId").references("id").inTable(TableName.SecretFolder).onDelete("SET NULL");
});
}
await createOnUpdateTrigger(knex, TableName.SecretFolderVersion);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.SecretFolderVersion);
await knex.schema.dropTableIfExists(TableName.SecretFolder);
await dropOnUpdateTrigger(knex, TableName.SecretFolder);
await dropOnUpdateTrigger(knex, TableName.SecretFolderVersion);
}

View File

@@ -1,30 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.SecretImport))) {
await knex.schema.createTable(TableName.SecretImport, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.integer("version").defaultTo(1);
t.string("importPath").notNullable();
t.uuid("importEnv").notNullable();
t.foreign("importEnv").references("id").inTable(TableName.Environment).onDelete("CASCADE");
t.integer("position").notNullable();
t.timestamps(true, true, true);
t.uuid("folderId").notNullable();
t.foreign("folderId").references("id").inTable(TableName.SecretFolder).onDelete("CASCADE");
t.unique(["folderId", "position"], {
indexName: "import_pos_composite_uniqe",
deferrable: "deferred"
});
});
}
await createOnUpdateTrigger(knex, TableName.SecretImport);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.SecretImport);
await dropOnUpdateTrigger(knex, TableName.SecretImport);
}

View File

@@ -1,26 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.SecretTag))) {
await knex.schema.createTable(TableName.SecretTag, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("name").notNullable();
t.string("slug").notNullable();
t.string("color");
t.timestamps(true, true, true);
t.uuid("createdBy");
t.foreign("createdBy").references("id").inTable(TableName.Users).onDelete("SET NULL");
t.string("projectId").notNullable();
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
});
}
await createOnUpdateTrigger(knex, TableName.SecretTag);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.SecretTag);
await dropOnUpdateTrigger(knex, TableName.SecretTag);
}

View File

@@ -1,93 +0,0 @@
import { Knex } from "knex";
import {
SecretEncryptionAlgo,
SecretKeyEncoding,
SecretType,
TableName,
} from "../schemas";
import {
createJunctionTable,
createOnUpdateTrigger,
dropOnUpdateTrigger,
} from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.SecretBlindIndex))) {
await knex.schema.createTable(TableName.SecretBlindIndex, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.text("encryptedSaltCipherText").notNullable();
t.text("saltIV").notNullable();
t.text("saltTag").notNullable();
t.string("algorithm")
.notNullable()
.defaultTo(SecretEncryptionAlgo.AES_256_GCM);
t.string("keyEncoding").notNullable().defaultTo(SecretKeyEncoding.UTF8);
t.string("projectId").notNullable().unique();
t.foreign("projectId")
.references("id")
.inTable(TableName.Project)
.onDelete("CASCADE");
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.SecretBlindIndex);
if (!(await knex.schema.hasTable(TableName.Secret))) {
await knex.schema.createTable(TableName.Secret, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.integer("version").defaultTo(1).notNullable();
t.string("type").notNullable().defaultTo(SecretType.Shared);
// t.text("secretKeyHash").notNullable();
// t.text("secretValueHash");
// t.text("secretCommentHash");
// this is required but for backward compatiability we are making it nullable
t.text("secretBlindIndex");
t.text("secretKeyCiphertext").notNullable();
t.text("secretKeyIV").notNullable();
t.text("secretKeyTag").notNullable();
t.text("secretValueCiphertext").notNullable();
t.text("secretValueIV").notNullable(); // symmetric encryption
t.text("secretValueTag").notNullable();
t.text("secretCommentCiphertext");
t.text("secretCommentIV");
t.text("secretCommentTag");
t.string("secretReminderNote");
t.integer("secretReminderRepeatDays");
t.boolean("skipMultilineEncoding").defaultTo(false);
t.string("algorithm")
.notNullable()
.defaultTo(SecretEncryptionAlgo.AES_256_GCM);
t.string("keyEncoding").notNullable().defaultTo(SecretKeyEncoding.UTF8);
t.jsonb("metadata");
t.uuid("userId");
t.foreign("userId")
.references("id")
.inTable(TableName.Users)
.onDelete("CASCADE");
t.uuid("folderId").notNullable();
t.foreign("folderId")
.references("id")
.inTable(TableName.SecretFolder)
.onDelete("CASCADE");
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.Secret);
// many to many relation between tags
await createJunctionTable(
knex,
TableName.JnSecretTag,
TableName.Secret,
TableName.SecretTag,
);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.SecretBlindIndex);
await dropOnUpdateTrigger(knex, TableName.SecretBlindIndex);
await knex.schema.dropTableIfExists(TableName.JnSecretTag);
await knex.schema.dropTableIfExists(TableName.Secret);
await dropOnUpdateTrigger(knex, TableName.Secret);
}

View File

@@ -1,53 +0,0 @@
import { Knex } from "knex";
import { SecretEncryptionAlgo, SecretKeyEncoding, SecretType, TableName } from "../schemas";
import { createJunctionTable, createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.SecretVersion))) {
await knex.schema.createTable(TableName.SecretVersion, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.integer("version").defaultTo(1).notNullable();
t.string("type").notNullable().defaultTo(SecretType.Shared);
t.text("secretBlindIndex");
t.text("secretKeyCiphertext").notNullable();
t.text("secretKeyIV").notNullable();
t.text("secretKeyTag").notNullable();
t.text("secretValueCiphertext").notNullable();
t.text("secretValueIV").notNullable(); // symmetric encryption
t.text("secretValueTag").notNullable();
t.text("secretCommentCiphertext");
t.text("secretCommentIV");
t.text("secretCommentTag");
t.string("secretReminderNote");
t.integer("secretReminderRepeatDays");
t.boolean("skipMultilineEncoding").defaultTo(false);
t.string("algorithm").notNullable().defaultTo(SecretEncryptionAlgo.AES_256_GCM);
t.string("keyEncoding").notNullable().defaultTo(SecretKeyEncoding.UTF8);
t.jsonb("metadata");
// to avoid orphan rows
t.uuid("envId");
t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE");
t.uuid("secretId").notNullable();
t.uuid("folderId").notNullable();
// t.foreign("secretId").references("id").inTable(TableName.Secret).onDelete("SET NULL");
t.uuid("userId");
t.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE");
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.SecretVersion);
// many to many relation between tags
await createJunctionTable(
knex,
TableName.SecretVersionTag,
TableName.SecretVersion,
TableName.SecretTag
);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.SecretVersionTag);
await knex.schema.dropTableIfExists(TableName.SecretVersion);
await dropOnUpdateTrigger(knex, TableName.SecretVersion);
}

View File

@@ -1,35 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.ProjectBot))) {
await knex.schema.createTable(TableName.ProjectBot, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("name").notNullable();
t.boolean("isActive").defaultTo(false).notNullable();
t.text("encryptedPrivateKey").notNullable();
t.text("publicKey").notNullable();
t.text("iv").notNullable();
t.text("tag").notNullable();
t.string("algorithm").notNullable();
t.string("keyEncoding").notNullable();
t.text("encryptedProjectKey");
t.text("encryptedProjectKeyNonce");
// one to one relationship
t.string("projectId").notNullable().unique();
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
t.uuid("senderId");
t.foreign("senderId").references("id").inTable(TableName.Users).onDelete("SET NULL");
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.ProjectBot);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.ProjectBot);
await dropOnUpdateTrigger(knex, TableName.ProjectBot);
}

View File

@@ -1,71 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.IntegrationAuth))) {
await knex.schema.createTable(TableName.IntegrationAuth, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("integration").notNullable();
t.string("teamId"); // vercel-specific
t.string("url"); // for self hosted
t.string("namespace"); // hashicorp specific
t.string("accountId"); // netlify
t.text("refreshCiphertext");
t.string("refreshIV");
t.string("refreshTag");
t.string("accessIdCiphertext");
t.string("accessIdIV");
t.string("accessIdTag");
t.text("accessCiphertext");
t.string("accessIV");
t.string("accessTag");
t.datetime("accessExpiresAt");
t.jsonb("metadata");
t.string("algorithm").notNullable();
t.string("keyEncoding").notNullable();
t.string("projectId").notNullable();
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.IntegrationAuth);
if (!(await knex.schema.hasTable(TableName.Integration))) {
await knex.schema.createTable(TableName.Integration, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.boolean("isActive").notNullable();
t.string("url"); // self hosted
t.string("app"); // name of app in provider
t.string("appId");
t.string("targetEnvironment");
t.string("targetEnvironmentId");
t.string("targetService"); // railway - qovery specific
t.string("targetServiceId");
t.string("owner"); // github specific
t.string("path"); // aws parameter store / vercel preview branch
t.string("region"); // aws
t.string("scope"); // qovery specific scope
t.string("integration").notNullable();
t.jsonb("metadata");
t.uuid("integrationAuthId").notNullable();
t.foreign("integrationAuthId")
.references("id")
.inTable(TableName.IntegrationAuth)
.onDelete("CASCADE");
t.uuid("envId").notNullable();
t.string("secretPath").defaultTo("/").notNullable();
t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE");
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.Integration);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.Integration);
await knex.schema.dropTableIfExists(TableName.IntegrationAuth);
await dropOnUpdateTrigger(knex, TableName.IntegrationAuth);
await dropOnUpdateTrigger(knex, TableName.Integration);
}

View File

@@ -1,32 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.ServiceToken))) {
await knex.schema.createTable(TableName.ServiceToken, (t) => {
t.string("id", 36).primary().defaultTo(knex.fn.uuid());
t.string("name").notNullable();
t.jsonb("scopes").notNullable();
t.specificType("permissions", "text[]").notNullable();
t.datetime("lastUsed");
t.datetime("expiresAt");
t.text("secretHash").notNullable();
t.text("encryptedKey");
t.text("iv");
t.text("tag");
t.timestamps(true, true, true);
// user is old one
t.string("createdBy").notNullable();
t.string("projectId").notNullable();
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
});
}
await createOnUpdateTrigger(knex, TableName.ServiceToken);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.ServiceToken);
await dropOnUpdateTrigger(knex, TableName.ServiceToken);
}

View File

@@ -1,32 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.Webhook))) {
await knex.schema.createTable(TableName.Webhook, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("secretPath").notNullable().defaultTo("/");
t.string("url").notNullable();
t.string("lastStatus");
t.text("lastRunErrorMessage");
t.boolean("isDisabled").defaultTo(false).notNullable();
// webhook signature
t.text("encryptedSecretKey");
t.text("iv");
t.text("tag");
t.string("algorithm");
t.string("keyEncoding");
t.timestamps(true, true, true);
t.uuid("envId").notNullable();
t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE");
});
}
await createOnUpdateTrigger(knex, TableName.Webhook);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.Webhook);
await dropOnUpdateTrigger(knex, TableName.Webhook);
}

View File

@@ -1,21 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.Identity))) {
await knex.schema.createTable(TableName.Identity, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("name").notNullable();
t.string("authMethod");
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.Identity);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.Identity);
await dropOnUpdateTrigger(knex, TableName.Identity);
}

View File

@@ -1,49 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.IdentityUniversalAuth))) {
await knex.schema.createTable(TableName.IdentityUniversalAuth, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("clientId").notNullable();
t.bigint("accessTokenTTL").defaultTo(7200).notNullable();
t.bigint("accessTokenMaxTTL").defaultTo(7200).notNullable();
t.bigint("accessTokenNumUsesLimit").defaultTo(0).notNullable();
t.jsonb("clientSecretTrustedIps").notNullable();
t.jsonb("accessTokenTrustedIps").notNullable();
t.timestamps(true, true, true);
t.uuid("identityId").notNullable().unique();
t.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE");
});
}
if (!(await knex.schema.hasTable(TableName.IdentityUaClientSecret))) {
await knex.schema.createTable(TableName.IdentityUaClientSecret, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("description").notNullable();
t.string("clientSecretPrefix").notNullable();
t.string("clientSecretHash").notNullable();
t.datetime("clientSecretLastUsedAt");
t.bigint("clientSecretNumUses").defaultTo(0).notNullable();
t.bigint("clientSecretNumUsesLimit").defaultTo(0).notNullable();
t.bigint("clientSecretTTL").defaultTo(0).notNullable();
t.boolean("isClientSecretRevoked").defaultTo(false).notNullable();
t.timestamps(true, true, true);
t.uuid("identityUAId").notNullable();
t.foreign("identityUAId")
.references("id")
.inTable(TableName.IdentityUniversalAuth)
.onDelete("CASCADE");
});
}
await createOnUpdateTrigger(knex, TableName.IdentityUniversalAuth);
await createOnUpdateTrigger(knex, TableName.IdentityUaClientSecret);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.IdentityUaClientSecret);
await knex.schema.dropTableIfExists(TableName.IdentityUniversalAuth);
await dropOnUpdateTrigger(knex, TableName.IdentityUaClientSecret);
await dropOnUpdateTrigger(knex, TableName.IdentityUniversalAuth);
}

View File

@@ -1,37 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.IdentityAccessToken))) {
await knex.schema.createTable(TableName.IdentityAccessToken, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.integer("accessTokenTTL").defaultTo(2592000).notNullable(); // 30 days second
t.integer("accessTokenMaxTTL").defaultTo(2592000).notNullable();
t.integer("accessTokenNumUses").defaultTo(0).notNullable();
t.integer("accessTokenNumUsesLimit").defaultTo(0).notNullable();
t.datetime("accessTokenLastUsedAt");
t.datetime("accessTokenLastRenewedAt");
t.boolean("isAccessTokenRevoked").defaultTo(false).notNullable();
t.uuid("identityUAClientSecretId");
t.foreign("identityUAClientSecretId")
.references("id")
.inTable(TableName.IdentityUaClientSecret)
.onDelete("CASCADE");
t.uuid("identityId").notNullable();
t.foreign("identityId")
.references("id")
.inTable(TableName.Identity)
.onDelete("CASCADE");
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.IdentityAccessToken);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.IdentityAccessToken);
await dropOnUpdateTrigger(knex, TableName.IdentityAccessToken);
}

View File

@@ -1,44 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.IdentityOrgMembership))) {
await knex.schema.createTable(TableName.IdentityOrgMembership, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("role").notNullable();
t.uuid("roleId");
t.foreign("roleId").references("id").inTable(TableName.OrgRoles);
t.uuid("orgId").notNullable();
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
t.timestamps(true, true, true);
t.uuid("identityId").notNullable();
t.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE");
});
}
await createOnUpdateTrigger(knex, TableName.IdentityOrgMembership);
if (!(await knex.schema.hasTable(TableName.IdentityProjectMembership))) {
await knex.schema.createTable(TableName.IdentityProjectMembership, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("role").notNullable();
t.uuid("roleId");
t.foreign("roleId").references("id").inTable(TableName.ProjectRoles);
t.string("projectId").notNullable();
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
t.uuid("identityId").notNullable();
t.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE");
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.IdentityProjectMembership);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.IdentityOrgMembership);
await knex.schema.dropTableIfExists(TableName.IdentityProjectMembership);
await dropOnUpdateTrigger(knex, TableName.IdentityProjectMembership);
await dropOnUpdateTrigger(knex, TableName.IdentityOrgMembership);
}

View File

@@ -1,45 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.SecretApprovalPolicy))) {
await knex.schema.createTable(TableName.SecretApprovalPolicy, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("name").notNullable();
t.string("secretPath");
t.integer("approvals").defaultTo(1).notNullable();
t.uuid("envId").notNullable();
t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE");
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.SecretApprovalPolicy);
if (!(await knex.schema.hasTable(TableName.SecretApprovalPolicyApprover))) {
await knex.schema.createTable(TableName.SecretApprovalPolicyApprover, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.uuid("approverId").notNullable();
t.foreign("approverId")
.references("id")
.inTable(TableName.ProjectMembership)
.onDelete("CASCADE");
t.uuid("policyId").notNullable();
t.foreign("policyId")
.references("id")
.inTable(TableName.SecretApprovalPolicy)
.onDelete("CASCADE");
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.SecretApprovalPolicyApprover);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.SecretApprovalPolicyApprover);
await knex.schema.dropTableIfExists(TableName.SecretApprovalPolicy);
await dropOnUpdateTrigger(knex, TableName.SecretApprovalPolicyApprover);
await dropOnUpdateTrigger(knex, TableName.SecretApprovalPolicy);
}

View File

@@ -1,118 +0,0 @@
import { Knex } from "knex";
import { SecretEncryptionAlgo, SecretKeyEncoding, TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.SecretApprovalRequest))) {
await knex.schema.createTable(TableName.SecretApprovalRequest, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.uuid("policyId").notNullable();
t.boolean("hasMerged").defaultTo(false).notNullable();
t.string("status").defaultTo("open").notNullable();
t.jsonb("conflicts");
t.foreign("policyId")
.references("id")
.inTable(TableName.SecretApprovalPolicy)
.onDelete("CASCADE");
t.string("slug").notNullable();
t.uuid("folderId").notNullable();
t.foreign("folderId").references("id").inTable(TableName.SecretFolder).onDelete("CASCADE");
t.uuid("statusChangeBy");
t.foreign("statusChangeBy")
.references("id")
.inTable(TableName.ProjectMembership)
.onDelete("SET NULL");
t.uuid("committerId").notNullable();
t.foreign("committerId")
.references("id")
.inTable(TableName.ProjectMembership)
.onDelete("CASCADE");
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.SecretApprovalRequest);
if (!(await knex.schema.hasTable(TableName.SecretApprovalRequestReviewer))) {
await knex.schema.createTable(TableName.SecretApprovalRequestReviewer, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.uuid("member").notNullable();
t.foreign("member").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
t.string("status").notNullable();
t.uuid("requestId").notNullable();
t.foreign("requestId")
.references("id")
.inTable(TableName.SecretApprovalRequest)
.onDelete("CASCADE");
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.SecretApprovalRequestReviewer);
if (!(await knex.schema.hasTable(TableName.SecretApprovalRequestSecret))) {
await knex.schema.createTable(TableName.SecretApprovalRequestSecret, (t) => {
// everything related to secret
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.integer("version").defaultTo(1);
t.text("secretBlindIndex");
t.text("secretKeyCiphertext").notNullable();
t.text("secretKeyIV").notNullable();
t.text("secretKeyTag").notNullable();
t.text("secretValueCiphertext").notNullable();
t.text("secretValueIV").notNullable(); // symmetric encryption
t.text("secretValueTag").notNullable();
t.text("secretCommentCiphertext");
t.text("secretCommentIV");
t.text("secretCommentTag");
t.string("secretReminderNote");
t.integer("secretReminderRepeatDays");
t.boolean("skipMultilineEncoding").defaultTo(false);
t.string("algorithm").notNullable().defaultTo(SecretEncryptionAlgo.AES_256_GCM);
t.string("keyEncoding").notNullable().defaultTo(SecretKeyEncoding.UTF8);
t.jsonb("metadata");
t.timestamps(true, true, true);
// commit details
t.uuid("requestId").notNullable();
t.foreign("requestId")
.references("id")
.inTable(TableName.SecretApprovalRequest)
.onDelete("CASCADE");
t.string("op").notNullable();
t.uuid("secretId");
t.foreign("secretId").references("id").inTable(TableName.Secret).onDelete("SET NULL");
t.uuid("secretVersion");
t.foreign("secretVersion")
.references("id")
.inTable(TableName.SecretVersion)
.onDelete("SET NULL");
});
}
await createOnUpdateTrigger(knex, TableName.SecretApprovalRequestSecret);
if (!(await knex.schema.hasTable(TableName.SecretApprovalRequestSecretTag))) {
await knex.schema.createTable(TableName.SecretApprovalRequestSecretTag, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.uuid("secretId").notNullable();
t.foreign("secretId")
.references("id")
.inTable(TableName.SecretApprovalRequestSecret)
.onDelete("CASCADE");
t.uuid("tagId").notNullable();
t.foreign("tagId").references("id").inTable(TableName.SecretTag).onDelete("CASCADE");
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.SecretApprovalRequestSecretTag);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.SecretApprovalRequestSecretTag);
await knex.schema.dropTableIfExists(TableName.SecretApprovalRequestSecret);
await knex.schema.dropTableIfExists(TableName.SecretApprovalRequestReviewer);
await knex.schema.dropTableIfExists(TableName.SecretApprovalRequest);
await dropOnUpdateTrigger(knex, TableName.SecretApprovalRequestSecretTag);
await dropOnUpdateTrigger(knex, TableName.SecretApprovalRequestSecret);
await dropOnUpdateTrigger(knex, TableName.SecretApprovalRequestReviewer);
await dropOnUpdateTrigger(knex, TableName.SecretApprovalRequest);
}

View File

@@ -1,47 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.SecretRotation))) {
await knex.schema.createTable(TableName.SecretRotation, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("provider").notNullable();
t.string("secretPath").notNullable();
t.integer("interval").notNullable();
t.datetime("lastRotatedAt");
t.string("status");
t.text("statusMessage");
t.text("encryptedData");
t.text("encryptedDataIV");
t.text("encryptedDataTag");
t.string("algorithm");
t.string("keyEncoding");
t.uuid("envId").notNullable();
t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE");
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.SecretRotation);
if (!(await knex.schema.hasTable(TableName.SecretRotationOutput))) {
await knex.schema.createTable(TableName.SecretRotationOutput, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("key").notNullable();
t.uuid("secretId").notNullable();
t.foreign("secretId").references("id").inTable(TableName.Secret).onDelete("CASCADE");
t.uuid("rotationId").notNullable();
t.foreign("rotationId")
.references("id")
.inTable(TableName.SecretRotation)
.onDelete("CASCADE");
});
}
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.SecretRotationOutput);
await knex.schema.dropTableIfExists(TableName.SecretRotation);
await dropOnUpdateTrigger(knex, TableName.SecretRotation);
}

View File

@@ -1,61 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.Snapshot))) {
await knex.schema.createTable(TableName.Snapshot, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.uuid("envId").notNullable();
t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE");
// this is not a relation kept like that
// this ensure snapshot are not lost when folder gets deleted and rolled back
t.uuid("folderId").notNullable();
t.uuid("parentFolderId");
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.Snapshot);
if (!(await knex.schema.hasTable(TableName.SnapshotSecret))) {
await knex.schema.createTable(TableName.SnapshotSecret, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.uuid("envId").notNullable();
t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE");
// not a relation kept like that to keep it when rolled back
t.uuid("secretVersionId").notNullable();
t.foreign("secretVersionId")
.references("id")
.inTable(TableName.SecretVersion)
.onDelete("CASCADE");
t.uuid("snapshotId").notNullable();
t.foreign("snapshotId").references("id").inTable(TableName.Snapshot).onDelete("CASCADE");
t.timestamps(true, true, true);
});
}
if (!(await knex.schema.hasTable(TableName.SnapshotFolder))) {
await knex.schema.createTable(TableName.SnapshotFolder, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.uuid("envId").notNullable();
t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE");
// not a relation kept like that to keep it when rolled back
t.uuid("folderVersionId").notNullable();
t.foreign("folderVersionId")
.references("id")
.inTable(TableName.SecretFolderVersion)
.onDelete("CASCADE");
t.uuid("snapshotId").notNullable();
t.foreign("snapshotId").references("id").inTable(TableName.Snapshot).onDelete("CASCADE");
t.timestamps(true, true, true);
});
}
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.SnapshotSecret);
await knex.schema.dropTableIfExists(TableName.SnapshotFolder);
await knex.schema.dropTableIfExists(TableName.Snapshot);
await dropOnUpdateTrigger(knex, TableName.Snapshot);
}

View File

@@ -1,33 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.SamlConfig))) {
await knex.schema.createTable(TableName.SamlConfig, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("authProvider").notNullable();
t.boolean("isActive").notNullable();
t.string("encryptedEntryPoint");
t.string("entryPointIV");
t.string("entryPointTag");
t.string("encryptedIssuer");
t.string("issuerTag");
t.string("issuerIV");
t.text("encryptedCert");
t.string("certIV");
t.string("certTag");
t.timestamps(true, true, true);
t.uuid("orgId").notNullable().unique();
t.foreign("orgId").references("id").inTable(TableName.Organization);
});
}
await createOnUpdateTrigger(knex, TableName.SamlConfig);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.SamlConfig);
await dropOnUpdateTrigger(knex, TableName.SamlConfig);
}

View File

@@ -1,35 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.OrgBot))) {
await knex.schema.createTable(TableName.OrgBot, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("name").notNullable();
t.text("publicKey").notNullable();
t.text("encryptedSymmetricKey").notNullable();
t.text("symmetricKeyIV").notNullable();
t.text("symmetricKeyTag").notNullable();
t.string("symmetricKeyAlgorithm").notNullable();
t.string("symmetricKeyKeyEncoding").notNullable();
t.text("encryptedPrivateKey").notNullable();
t.text("privateKeyIV").notNullable();
t.text("privateKeyTag").notNullable();
t.string("privateKeyAlgorithm").notNullable();
t.string("privateKeyKeyEncoding").notNullable();
// one to one relationship
t.uuid("orgId").notNullable().unique();
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.OrgBot);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.OrgBot);
await dropOnUpdateTrigger(knex, TableName.OrgBot);
}

View File

@@ -1,29 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.AuditLog))) {
await knex.schema.createTable(TableName.AuditLog, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("actor").notNullable();
t.jsonb("actorMetadata").notNullable();
t.string("ipAddress");
t.string("eventType").notNullable();
t.jsonb("eventMetadata");
t.string("userAgent");
t.string("userAgentType");
t.datetime("expiresAt");
t.timestamps(true, true, true);
// no trigger needed as this collection is append only
t.uuid("orgId");
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
t.string("projectId");
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
});
}
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.AuditLog);
}

View File

@@ -1,79 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.GitAppInstallSession))) {
await knex.schema.createTable(TableName.GitAppInstallSession, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("sessionId").notNullable().unique();
t.uuid("userId");
// one to one relationship
t.uuid("orgId").notNullable().unique();
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
t.timestamps(true, true, true);
});
}
createOnUpdateTrigger(knex, TableName.GitAppInstallSession);
if (!(await knex.schema.hasTable(TableName.GitAppOrg))) {
await knex.schema.createTable(TableName.GitAppOrg, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("installationId").notNullable().unique();
t.uuid("userId").notNullable();
// one to one relationship
t.uuid("orgId").notNullable().unique();
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
t.timestamps(true, true, true);
});
}
createOnUpdateTrigger(knex, TableName.GitAppOrg);
if (!(await knex.schema.hasTable(TableName.SecretScanningGitRisk))) {
await knex.schema.createTable(TableName.SecretScanningGitRisk, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("description");
t.string("startLine");
t.string("endLine");
t.string("startColumn");
t.string("endColumn");
t.string("file");
t.string("symlinkFile");
t.string("commit");
t.string("entropy");
t.string("author");
t.string("email");
t.string("date");
t.text("message");
t.specificType("tags", "text[]");
t.string("ruleID");
t.string("fingerprint").unique();
t.string("fingerPrintWithoutCommitId");
t.boolean("isFalsePositive").defaultTo(false);
t.boolean("isResolved").defaultTo(false);
t.string("riskOwner");
t.string("installationId").notNullable();
t.string("repositoryId");
t.string("repositoryLink");
t.string("repositoryFullName");
t.string("pusherName");
t.string("pusherEmail");
t.string("status");
// one to one relationship
t.uuid("orgId").notNullable();
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
t.timestamps(true, true, true);
});
}
createOnUpdateTrigger(knex, TableName.SecretScanningGitRisk);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.SecretScanningGitRisk);
await knex.schema.dropTableIfExists(TableName.GitAppOrg);
await knex.schema.dropTableIfExists(TableName.GitAppInstallSession);
await dropOnUpdateTrigger(knex, TableName.SecretScanningGitRisk);
await dropOnUpdateTrigger(knex, TableName.GitAppOrg);
await dropOnUpdateTrigger(knex, TableName.GitAppInstallSession);
}

View File

@@ -1,26 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.TrustedIps))) {
await knex.schema.createTable(TableName.TrustedIps, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("ipAddress").notNullable();
t.string("type").notNullable();
t.integer("prefix");
t.boolean("isActive").defaultTo(true);
t.string("comment");
t.string("projectId").notNullable();
t.foreign("projectId").references("id").inTable(TableName.Project);
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.TrustedIps);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.TrustedIps);
await dropOnUpdateTrigger(knex, TableName.TrustedIps);
}

View File

@@ -1,39 +0,0 @@
import { Schema, Types, model } from "mongoose";
export interface IAPIKeyData {
_id: Types.ObjectId;
name: string;
user: Types.ObjectId;
lastUsed: Date;
expiresAt: Date;
secretHash: string;
}
const apiKeyDataSchema = new Schema<IAPIKeyData>(
{
name: {
type: String,
required: true,
},
user: {
type: Schema.Types.ObjectId,
ref: "User",
required: true,
},
lastUsed: {
type: Date,
},
expiresAt: {
type: Date,
},
secretHash: {
type: String,
required: true,
},
},
{
timestamps: true,
},
);
export const APIKeyData = model<IAPIKeyData>("APIKeyData", apiKeyDataSchema);

View File

@@ -1,38 +0,0 @@
import { Document, Schema, Types, model } from "mongoose";
export interface IAPIKeyDataV2 extends Document {
_id: Types.ObjectId;
name: string;
user: Types.ObjectId;
lastUsed?: Date
usageCount: number;
expiresAt?: Date;
}
const apiKeyDataV2Schema = new Schema(
{
name: {
type: String,
required: true
},
user: {
type: Schema.Types.ObjectId,
ref: "User",
required: true
},
lastUsed: {
type: Date,
required: false
},
usageCount: {
type: Number,
default: 0,
required: true
}
},
{
timestamps: true
}
);
export const APIKeyDataV2 = model<IAPIKeyDataV2>("APIKeyDataV2", apiKeyDataV2Schema);

View File

@@ -1,70 +0,0 @@
import { Schema, Types, model } from "mongoose";
import { ActorType, EventType, UserAgentType } from "./enums";
import { Actor, Event } from "./types";
export interface IAuditLog {
actor: Actor;
organization: Types.ObjectId;
workspace: Types.ObjectId;
ipAddress: string;
event: Event;
userAgent: string;
userAgentType: UserAgentType;
expiresAt?: Date;
}
const auditLogSchema = new Schema<IAuditLog>(
{
actor: {
type: {
type: String,
enum: ActorType,
required: true,
},
metadata: {
type: Schema.Types.Mixed,
},
},
organization: {
type: Schema.Types.ObjectId,
required: false,
},
workspace: {
type: Schema.Types.ObjectId,
required: false,
index: true,
},
ipAddress: {
type: String,
required: true,
},
event: {
type: {
type: String,
enum: EventType,
required: true,
},
metadata: {
type: Schema.Types.Mixed,
},
},
userAgent: {
type: String,
required: true,
},
userAgentType: {
type: String,
enum: UserAgentType,
required: true,
},
expiresAt: {
type: Date,
expires: 0,
},
},
{
timestamps: true,
},
);
export const AuditLog = model<IAuditLog>("AuditLog", auditLogSchema);

View File

@@ -1,69 +0,0 @@
export enum ActorType { // would extend to AWS, Azure, ...
USER = "user", // userIdentity
SERVICE = "service",
IDENTITY = "identity"
}
export enum UserAgentType {
WEB = "web",
CLI = "cli",
K8_OPERATOR = "k8-operator",
TERRAFORM = "terraform",
OTHER = "other",
PYTHON_SDK = "InfisicalPythonSDK",
NODE_SDK = "InfisicalNodeSDK"
}
export enum EventType {
GET_SECRETS = "get-secrets",
GET_SECRET = "get-secret",
REVEAL_SECRET = "reveal-secret",
CREATE_SECRET = "create-secret",
CREATE_SECRETS = "create-secrets",
UPDATE_SECRET = "update-secret",
UPDATE_SECRETS = "update-secrets",
DELETE_SECRET = "delete-secret",
DELETE_SECRETS = "delete-secrets",
GET_WORKSPACE_KEY = "get-workspace-key",
AUTHORIZE_INTEGRATION = "authorize-integration",
UNAUTHORIZE_INTEGRATION = "unauthorize-integration",
CREATE_INTEGRATION = "create-integration",
DELETE_INTEGRATION = "delete-integration",
ADD_TRUSTED_IP = "add-trusted-ip",
UPDATE_TRUSTED_IP = "update-trusted-ip",
DELETE_TRUSTED_IP = "delete-trusted-ip",
CREATE_SERVICE_TOKEN = "create-service-token", // v2
DELETE_SERVICE_TOKEN = "delete-service-token", // v2
CREATE_IDENTITY = "create-identity",
UPDATE_IDENTITY = "update-identity",
DELETE_IDENTITY = "delete-identity",
LOGIN_IDENTITY_UNIVERSAL_AUTH = "login-identity-universal-auth",
ADD_IDENTITY_UNIVERSAL_AUTH = "add-identity-universal-auth",
UPDATE_IDENTITY_UNIVERSAL_AUTH = "update-identity-universal-auth",
GET_IDENTITY_UNIVERSAL_AUTH = "get-identity-universal-auth",
CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "create-identity-universal-auth-client-secret",
REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "revoke-identity-universal-auth-client-secret",
GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRETS = "get-identity-universal-auth-client-secret",
CREATE_ENVIRONMENT = "create-environment",
UPDATE_ENVIRONMENT = "update-environment",
DELETE_ENVIRONMENT = "delete-environment",
ADD_WORKSPACE_MEMBER = "add-workspace-member",
ADD_BATCH_WORKSPACE_MEMBER = "add-workspace-members",
REMOVE_WORKSPACE_MEMBER = "remove-workspace-member",
CREATE_FOLDER = "create-folder",
UPDATE_FOLDER = "update-folder",
DELETE_FOLDER = "delete-folder",
CREATE_WEBHOOK = "create-webhook",
UPDATE_WEBHOOK_STATUS = "update-webhook-status",
DELETE_WEBHOOK = "delete-webhook",
GET_SECRET_IMPORTS = "get-secret-imports",
CREATE_SECRET_IMPORT = "create-secret-import",
UPDATE_SECRET_IMPORT = "update-secret-import",
DELETE_SECRET_IMPORT = "delete-secret-import",
UPDATE_USER_WORKSPACE_ROLE = "update-user-workspace-role",
UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS = "update-user-workspace-denied-permissions",
SECRET_APPROVAL_MERGED = "secret-approval-merged",
SECRET_APPROVAL_REQUEST = "secret-approval-request",
SECRET_APPROVAL_CLOSED = "secret-approval-closed",
SECRET_APPROVAL_REOPENED = "secret-approval-reopened"
}

View File

@@ -1,3 +0,0 @@
export * from "./auditLog";
export * from "./enums";
export * from "./types";

View File

@@ -1,585 +0,0 @@
import { ActorType, EventType } from "./enums";
import { IIdentityTrustedIp } from "../../../models";
interface UserActorMetadata {
userId: string;
email: string;
}
interface ServiceActorMetadata {
serviceId: string;
name: string;
}
interface IdentityActorMetadata {
identityId: string;
name: string;
}
export interface UserActor {
type: ActorType.USER;
metadata: UserActorMetadata;
}
export interface ServiceActor {
type: ActorType.SERVICE;
metadata: ServiceActorMetadata;
}
export interface IdentityActor {
type: ActorType.IDENTITY;
metadata: IdentityActorMetadata;
}
export type Actor = UserActor | ServiceActor | IdentityActor;
interface GetSecretsEvent {
type: EventType.GET_SECRETS;
metadata: {
environment: string;
secretPath: string;
numberOfSecrets: number;
};
}
interface GetSecretEvent {
type: EventType.GET_SECRET;
metadata: {
environment: string;
secretPath: string;
secretId: string;
secretKey: string;
secretVersion: number;
};
}
interface CreateSecretEvent {
type: EventType.CREATE_SECRET;
metadata: {
environment: string;
secretPath: string;
secretId: string;
secretKey: string;
secretVersion: number;
};
}
interface CreateSecretBatchEvent {
type: EventType.CREATE_SECRETS;
metadata: {
environment: string;
secretPath: string;
secrets: Array<{ secretId: string; secretKey: string; secretVersion: number }>;
};
}
interface UpdateSecretEvent {
type: EventType.UPDATE_SECRET;
metadata: {
environment: string;
secretPath: string;
secretId: string;
secretKey: string;
secretVersion: number;
};
}
interface UpdateSecretBatchEvent {
type: EventType.UPDATE_SECRETS;
metadata: {
environment: string;
secretPath: string;
secrets: Array<{ secretId: string; secretKey: string; secretVersion: number }>;
};
}
interface DeleteSecretEvent {
type: EventType.DELETE_SECRET;
metadata: {
environment: string;
secretPath: string;
secretId: string;
secretKey: string;
secretVersion: number;
};
}
interface DeleteSecretBatchEvent {
type: EventType.DELETE_SECRETS;
metadata: {
environment: string;
secretPath: string;
secrets: Array<{ secretId: string; secretKey: string; secretVersion: number }>;
};
}
interface GetWorkspaceKeyEvent {
type: EventType.GET_WORKSPACE_KEY;
metadata: {
keyId: string;
};
}
interface AuthorizeIntegrationEvent {
type: EventType.AUTHORIZE_INTEGRATION;
metadata: {
integration: string;
};
}
interface UnauthorizeIntegrationEvent {
type: EventType.UNAUTHORIZE_INTEGRATION;
metadata: {
integration: string;
};
}
interface CreateIntegrationEvent {
type: EventType.CREATE_INTEGRATION;
metadata: {
integrationId: string;
integration: string; // TODO: fix type
environment: string;
secretPath: string;
url?: string;
app?: string;
appId?: string;
targetEnvironment?: string;
targetEnvironmentId?: string;
targetService?: string;
targetServiceId?: string;
path?: string;
region?: string;
};
}
interface DeleteIntegrationEvent {
type: EventType.DELETE_INTEGRATION;
metadata: {
integrationId: string;
integration: string; // TODO: fix type
environment: string;
secretPath: string;
url?: string;
app?: string;
appId?: string;
targetEnvironment?: string;
targetEnvironmentId?: string;
targetService?: string;
targetServiceId?: string;
path?: string;
region?: string;
};
}
interface AddTrustedIPEvent {
type: EventType.ADD_TRUSTED_IP;
metadata: {
trustedIpId: string;
ipAddress: string;
prefix?: number;
};
}
interface UpdateTrustedIPEvent {
type: EventType.UPDATE_TRUSTED_IP;
metadata: {
trustedIpId: string;
ipAddress: string;
prefix?: number;
};
}
interface DeleteTrustedIPEvent {
type: EventType.DELETE_TRUSTED_IP;
metadata: {
trustedIpId: string;
ipAddress: string;
prefix?: number;
};
}
interface CreateServiceTokenEvent {
type: EventType.CREATE_SERVICE_TOKEN;
metadata: {
name: string;
scopes: Array<{
environment: string;
secretPath: string;
}>;
};
}
interface DeleteServiceTokenEvent {
type: EventType.DELETE_SERVICE_TOKEN;
metadata: {
name: string;
scopes: Array<{
environment: string;
secretPath: string;
}>;
};
}
interface CreateIdentityEvent { // note: currently not logging org-role
type: EventType.CREATE_IDENTITY;
metadata: {
identityId: string;
name: string;
};
}
interface UpdateIdentityEvent {
type: EventType.UPDATE_IDENTITY;
metadata: {
identityId: string;
name?: string;
};
}
interface DeleteIdentityEvent {
type: EventType.DELETE_IDENTITY;
metadata: {
identityId: string;
};
}
interface LoginIdentityUniversalAuthEvent {
type: EventType.LOGIN_IDENTITY_UNIVERSAL_AUTH ;
metadata: {
identityId: string;
identityUniversalAuthId: string;
clientSecretId: string;
identityAccessTokenId: string;
};
}
interface AddIdentityUniversalAuthEvent {
type: EventType.ADD_IDENTITY_UNIVERSAL_AUTH;
metadata: {
identityId: string;
clientSecretTrustedIps: Array<IIdentityTrustedIp>;
accessTokenTTL: number;
accessTokenMaxTTL: number;
accessTokenNumUsesLimit: number;
accessTokenTrustedIps: Array<IIdentityTrustedIp>;
};
}
interface UpdateIdentityUniversalAuthEvent {
type: EventType.UPDATE_IDENTITY_UNIVERSAL_AUTH;
metadata: {
identityId: string;
clientSecretTrustedIps?: Array<IIdentityTrustedIp>;
accessTokenTTL?: number;
accessTokenMaxTTL?: number;
accessTokenNumUsesLimit?: number;
accessTokenTrustedIps?: Array<IIdentityTrustedIp>;
};
}
interface GetIdentityUniversalAuthEvent {
type: EventType.GET_IDENTITY_UNIVERSAL_AUTH;
metadata: {
identityId: string;
};
}
interface CreateIdentityUniversalAuthClientSecretEvent {
type: EventType.CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET ;
metadata: {
identityId: string;
clientSecretId: string;
};
}
interface GetIdentityUniversalAuthClientSecretsEvent {
type: EventType.GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRETS;
metadata: {
identityId: string;
};
}
interface RevokeIdentityUniversalAuthClientSecretEvent {
type: EventType.REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET ;
metadata: {
identityId: string;
clientSecretId: string;
};
}
interface CreateEnvironmentEvent {
type: EventType.CREATE_ENVIRONMENT;
metadata: {
name: string;
slug: string;
};
}
interface UpdateEnvironmentEvent {
type: EventType.UPDATE_ENVIRONMENT;
metadata: {
oldName: string;
newName: string;
oldSlug: string;
newSlug: string;
};
}
interface DeleteEnvironmentEvent {
type: EventType.DELETE_ENVIRONMENT;
metadata: {
name: string;
slug: string;
};
}
interface AddWorkspaceMemberEvent {
type: EventType.ADD_WORKSPACE_MEMBER;
metadata: {
userId: string;
email: string;
};
}
interface AddBatchWorkspaceMemberEvent {
type: EventType.ADD_BATCH_WORKSPACE_MEMBER;
metadata: Array<{
userId: string;
email: string;
}>;
}
interface RemoveWorkspaceMemberEvent {
type: EventType.REMOVE_WORKSPACE_MEMBER;
metadata: {
userId: string;
email: string;
};
}
interface CreateFolderEvent {
type: EventType.CREATE_FOLDER;
metadata: {
environment: string;
folderId: string;
folderName: string;
folderPath: string;
};
}
interface UpdateFolderEvent {
type: EventType.UPDATE_FOLDER;
metadata: {
environment: string;
folderId: string;
oldFolderName: string;
newFolderName: string;
folderPath: string;
};
}
interface DeleteFolderEvent {
type: EventType.DELETE_FOLDER;
metadata: {
environment: string;
folderId: string;
folderName: string;
folderPath: string;
};
}
interface CreateWebhookEvent {
type: EventType.CREATE_WEBHOOK;
metadata: {
webhookId: string;
environment: string;
secretPath: string;
webhookUrl: string;
isDisabled: boolean;
};
}
interface UpdateWebhookStatusEvent {
type: EventType.UPDATE_WEBHOOK_STATUS;
metadata: {
webhookId: string;
environment: string;
secretPath: string;
webhookUrl: string;
isDisabled: boolean;
};
}
interface DeleteWebhookEvent {
type: EventType.DELETE_WEBHOOK;
metadata: {
webhookId: string;
environment: string;
secretPath: string;
webhookUrl: string;
isDisabled: boolean;
};
}
interface GetSecretImportsEvent {
type: EventType.GET_SECRET_IMPORTS;
metadata: {
environment: string;
secretImportId: string;
folderId: string;
numberOfImports: number;
};
}
interface CreateSecretImportEvent {
type: EventType.CREATE_SECRET_IMPORT;
metadata: {
secretImportId: string;
folderId: string;
importFromEnvironment: string;
importFromSecretPath: string;
importToEnvironment: string;
importToSecretPath: string;
};
}
interface UpdateSecretImportEvent {
type: EventType.UPDATE_SECRET_IMPORT;
metadata: {
secretImportId: string;
folderId: string;
importToEnvironment: string;
importToSecretPath: string;
orderBefore: {
environment: string;
secretPath: string;
}[];
orderAfter: {
environment: string;
secretPath: string;
}[];
};
}
interface DeleteSecretImportEvent {
type: EventType.DELETE_SECRET_IMPORT;
metadata: {
secretImportId: string;
folderId: string;
importFromEnvironment: string;
importFromSecretPath: string;
importToEnvironment: string;
importToSecretPath: string;
};
}
interface UpdateUserRole {
type: EventType.UPDATE_USER_WORKSPACE_ROLE;
metadata: {
userId: string;
email: string;
oldRole: string;
newRole: string;
};
}
interface UpdateUserDeniedPermissions {
type: EventType.UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS;
metadata: {
userId: string;
email: string;
deniedPermissions: {
environmentSlug: string;
ability: string;
}[];
};
}
interface SecretApprovalMerge {
type: EventType.SECRET_APPROVAL_MERGED;
metadata: {
mergedBy: string;
secretApprovalRequestSlug: string;
secretApprovalRequestId: string;
};
}
interface SecretApprovalClosed {
type: EventType.SECRET_APPROVAL_CLOSED;
metadata: {
closedBy: string;
secretApprovalRequestSlug: string;
secretApprovalRequestId: string;
};
}
interface SecretApprovalReopened {
type: EventType.SECRET_APPROVAL_REOPENED;
metadata: {
reopenedBy: string;
secretApprovalRequestSlug: string;
secretApprovalRequestId: string;
};
}
interface SecretApprovalRequest {
type: EventType.SECRET_APPROVAL_REQUEST;
metadata: {
committedBy: string;
secretApprovalRequestSlug: string;
secretApprovalRequestId: string;
};
}
export type Event =
| GetSecretsEvent
| GetSecretEvent
| CreateSecretEvent
| CreateSecretBatchEvent
| UpdateSecretEvent
| UpdateSecretBatchEvent
| DeleteSecretEvent
| DeleteSecretBatchEvent
| GetWorkspaceKeyEvent
| AuthorizeIntegrationEvent
| UnauthorizeIntegrationEvent
| CreateIntegrationEvent
| DeleteIntegrationEvent
| AddTrustedIPEvent
| UpdateTrustedIPEvent
| DeleteTrustedIPEvent
| CreateServiceTokenEvent
| DeleteServiceTokenEvent
| CreateIdentityEvent
| UpdateIdentityEvent
| DeleteIdentityEvent
| LoginIdentityUniversalAuthEvent
| AddIdentityUniversalAuthEvent
| UpdateIdentityUniversalAuthEvent
| GetIdentityUniversalAuthEvent
| CreateIdentityUniversalAuthClientSecretEvent
| GetIdentityUniversalAuthClientSecretsEvent
| RevokeIdentityUniversalAuthClientSecretEvent
| CreateEnvironmentEvent
| UpdateEnvironmentEvent
| DeleteEnvironmentEvent
| AddWorkspaceMemberEvent
| AddBatchWorkspaceMemberEvent
| RemoveWorkspaceMemberEvent
| CreateFolderEvent
| UpdateFolderEvent
| DeleteFolderEvent
| CreateWebhookEvent
| UpdateWebhookStatusEvent
| DeleteWebhookEvent
| GetSecretImportsEvent
| CreateSecretImportEvent
| UpdateSecretImportEvent
| DeleteSecretImportEvent
| UpdateUserRole
| UpdateUserDeniedPermissions
| SecretApprovalMerge
| SecretApprovalClosed
| SecretApprovalRequest
| SecretApprovalReopened;

View File

@@ -1,67 +0,0 @@
import { Schema, Types, model } from "mongoose";
export interface IBackupPrivateKey {
_id: Types.ObjectId;
user: Types.ObjectId;
encryptedPrivateKey: string;
iv: string;
tag: string;
salt: string;
algorithm: string;
keyEncoding: "base64" | "utf8";
verifier: string;
createdAt: string;
updatedAt: string;
}
const backupPrivateKeySchema = new Schema<IBackupPrivateKey>(
{
user: {
type: Schema.Types.ObjectId,
ref: "User",
required: true,
},
encryptedPrivateKey: {
type: String,
required: true,
},
iv: {
type: String,
required: true,
},
tag: {
type: String,
required: true,
},
algorithm: {
// the encryption algorithm used
type: String,
required: true,
},
keyEncoding: {
type: String,
required: true,
},
salt: {
type: String,
required: true,
},
verifier: {
type: String,
required: true,
},
},
{
timestamps: true,
},
);
export const BackupPrivateKey = model<IBackupPrivateKey>(
"BackupPrivateKey",
backupPrivateKeySchema,
);

View File

@@ -1,68 +0,0 @@
import { Schema, Types, model } from "mongoose";
export interface IBot {
_id: Types.ObjectId;
name: string;
workspace: Types.ObjectId;
isActive: boolean;
publicKey: string;
encryptedPrivateKey: string;
iv: string;
tag: string;
algorithm: "aes-256-gcm";
keyEncoding: "base64" | "utf8";
}
const botSchema = new Schema<IBot>(
{
name: {
type: String,
required: true,
},
workspace: {
type: Schema.Types.ObjectId,
ref: "Workspace",
required: true,
},
isActive: {
type: Boolean,
required: true,
default: false,
},
publicKey: {
type: String,
required: true,
},
encryptedPrivateKey: {
type: String,
required: true,
},
iv: {
type: String,
required: true,
},
tag: {
type: String,
required: true,
},
algorithm: {
// the encryption algorithm used
type: String,
required: true,
},
keyEncoding: {
type: String,
required: true,
},
},
{
timestamps: true,
},
);
export const Bot = model<IBot>("Bot", botSchema);

View File

@@ -1,43 +0,0 @@
import { Schema, Types, model } from "mongoose";
export interface IBotKey {
_id: Types.ObjectId;
encryptedKey: string;
nonce: string;
sender: Types.ObjectId;
bot: Types.ObjectId;
workspace: Types.ObjectId;
}
const botKeySchema = new Schema<IBotKey>(
{
encryptedKey: {
type: String,
required: true,
},
nonce: {
type: String,
required: true,
},
sender: {
type: Schema.Types.ObjectId,
ref: "User",
required: true,
},
bot: {
type: Schema.Types.ObjectId,
ref: "Bot",
required: true,
},
workspace: {
type: Schema.Types.ObjectId,
ref: "Workspace",
required: true,
},
},
{
timestamps: true,
}
);
export const BotKey = model<IBotKey>("BotKey", botKeySchema);

View File

@@ -1,81 +0,0 @@
import { Schema, Types, model } from "mongoose";
export interface IBotOrg {
_id: Types.ObjectId;
name: string;
organization: Types.ObjectId;
publicKey: string;
encryptedSymmetricKey: string;
symmetricKeyIV: string;
symmetricKeyTag: string;
symmetricKeyAlgorithm: "aes-256-gcm";
symmetricKeyKeyEncoding: "base64" | "utf8";
encryptedPrivateKey: string;
privateKeyIV: string;
privateKeyTag: string;
privateKeyAlgorithm: "aes-256-gcm";
privateKeyKeyEncoding: "base64" | "utf8";
}
const botOrgSchema = new Schema<IBotOrg>(
{
name: {
type: String,
required: true,
},
organization: {
type: Schema.Types.ObjectId,
ref: "Organization",
required: true,
},
publicKey: {
type: String,
required: true,
},
encryptedSymmetricKey: {
type: String,
required: true,
},
symmetricKeyIV: {
type: String,
required: true,
},
symmetricKeyTag: {
type: String,
required: true,
},
symmetricKeyAlgorithm: {
type: String,
required: true,
},
symmetricKeyKeyEncoding: {
type: String,
required: true,
},
encryptedPrivateKey: {
type: String,
required: true,
},
privateKeyIV: {
type: String,
required: true,
},
privateKeyTag: {
type: String,
required: true,
},
privateKeyAlgorithm: {
type: String,
required: true,
},
privateKeyKeyEncoding: {
type: String,
required: true,
},
},
{
timestamps: true,
},
);
export const BotOrg = model<IBotOrg>("BotOrg", botOrgSchema);

View File

@@ -1,54 +0,0 @@
import { Schema, Types, model } from "mongoose";
export type TFolderRootSchema = {
_id: Types.ObjectId;
workspace: Types.ObjectId;
environment: string;
nodes: TFolderSchema;
};
export type TFolderSchema = {
id: string;
name: string;
version: number;
children: TFolderSchema[];
};
const folderSchema = new Schema<TFolderSchema>({
id: {
required: true,
type: String,
},
version: {
required: true,
type: Number,
default: 1,
},
name: {
required: true,
type: String,
default: "root",
},
});
folderSchema.add({ children: [folderSchema] });
const folderRootSchema = new Schema<TFolderRootSchema>(
{
workspace: {
type: Schema.Types.ObjectId,
ref: "Workspace",
required: true,
},
environment: {
type: String,
required: true,
},
nodes: folderSchema,
},
{
timestamps: true,
}
);
export const Folder = model<TFolderRootSchema>("Folder", folderRootSchema);

View File

@@ -1,58 +0,0 @@
import { Schema, Types, model } from "mongoose";
export type TFolderRootVersionSchema = {
_id: Types.ObjectId;
workspace: Types.ObjectId;
environment: string;
nodes: TFolderVersionSchema;
};
export type TFolderVersionSchema = {
id: string;
name: string;
version: number;
children: TFolderVersionSchema[];
};
const folderVersionSchema = new Schema<TFolderVersionSchema>({
id: {
required: true,
type: String,
default: "root",
},
name: {
required: true,
type: String,
default: "root",
},
version: {
required: true,
type: Number,
default: 1,
},
});
folderVersionSchema.add({ children: [folderVersionSchema] });
const folderRootVersionSchema = new Schema<TFolderRootVersionSchema>(
{
workspace: {
type: Schema.Types.ObjectId,
ref: "Workspace",
required: true,
},
environment: {
type: String,
required: true,
},
nodes: folderVersionSchema,
},
{
timestamps: true,
}
);
export const FolderVersion = model<TFolderRootVersionSchema>(
"FolderVersion",
folderRootVersionSchema
);

View File

@@ -1,32 +0,0 @@
import { Schema, Types, model } from "mongoose";
type GitAppInstallationSession = {
id: string;
sessionId: string;
organization: Types.ObjectId;
user: Types.ObjectId;
}
const gitAppInstallationSession = new Schema<GitAppInstallationSession>({
id: {
required: true,
type: String,
},
sessionId: {
type: String,
required: true,
unique: true
},
organization: {
type: Schema.Types.ObjectId,
required: true,
unique: true
},
user: {
type: Schema.Types.ObjectId,
ref: "User"
}
});
export const GitAppInstallationSession = model<GitAppInstallationSession>("git_app_installation_session", gitAppInstallationSession);

View File

@@ -1,29 +0,0 @@
import { Schema, model } from "mongoose";
type Installation = {
installationId: string
organizationId: string
user: Schema.Types.ObjectId
};
const gitAppOrganizationInstallation = new Schema<Installation>({
installationId: {
type: String,
required: true,
unique: true
},
organizationId: {
type: String,
required: true,
unique: true
},
user: {
type: Schema.Types.ObjectId,
ref: "User",
required: true,
}
});
export const GitAppOrganizationInstallation = model<Installation>("git_app_organization_installation", gitAppOrganizationInstallation);

View File

@@ -1,150 +0,0 @@
import { Schema, model } from "mongoose";
export const STATUS_RESOLVED_FALSE_POSITIVE = "RESOLVED_FALSE_POSITIVE";
export const STATUS_RESOLVED_REVOKED = "RESOLVED_REVOKED";
export const STATUS_RESOLVED_NOT_REVOKED = "RESOLVED_NOT_REVOKED";
export const STATUS_UNRESOLVED = "UNRESOLVED";
export type IGitRisks = {
id: string;
description: string;
startLine: string;
endLine: string;
startColumn: string;
endColumn: string;
match: string;
secret: string;
file: string;
symlinkFile: string;
commit: string;
entropy: string;
author: string;
email: string;
date: string;
message: string;
tags: string[];
ruleID: string;
fingerprint: string;
fingerPrintWithoutCommitId: string
isFalsePositive: boolean; // New field for marking risks as false positives
isResolved: boolean; // New field for marking risks as resolved
riskOwner: string | null; // New field for setting a risk owner (nullable string)
installationId: string,
repositoryId: string,
repositoryLink: string
repositoryFullName: string
status: string
pusher: {
name: string,
email: string
},
organization: Schema.Types.ObjectId,
}
const gitRisks = new Schema<IGitRisks>({
id: {
type: String,
},
description: {
type: String,
},
startLine: {
type: String,
},
endLine: {
type: String,
},
startColumn: {
type: String,
},
endColumn: {
type: String,
},
file: {
type: String,
},
symlinkFile: {
type: String,
},
commit: {
type: String,
},
entropy: {
type: String,
},
author: {
type: String,
},
email: {
type: String,
},
date: {
type: String,
},
message: {
type: String,
},
tags: {
type: [String],
},
ruleID: {
type: String,
},
fingerprint: {
type: String,
unique: true
},
fingerPrintWithoutCommitId: {
type: String,
},
isFalsePositive: {
type: Boolean,
default: false
},
isResolved: {
type: Boolean,
default: false
},
riskOwner: {
type: String,
default: null
},
installationId: {
type: String,
require: true
},
repositoryId: {
type: String
},
repositoryLink: {
type: String
},
repositoryFullName: {
type: String
},
pusher: {
name: {
type: String
},
email: {
type: String
},
},
organization: {
type: Schema.Types.ObjectId,
ref: "Organization",
},
status: {
type: String,
enum: [
STATUS_RESOLVED_FALSE_POSITIVE,
STATUS_RESOLVED_REVOKED,
STATUS_RESOLVED_NOT_REVOKED,
STATUS_UNRESOLVED
],
default: STATUS_UNRESOLVED
}
}, { timestamps: true });
export const GitRisks = model<IGitRisks>("GitRisks", gitRisks);

View File

@@ -1,38 +0,0 @@
import { Document, Schema, Types, model } from "mongoose";
import { IPType } from "../ee/models";
export interface IIdentityTrustedIp {
ipAddress: string;
type: IPType;
prefix: number;
}
export enum IdentityAuthMethod {
UNIVERSAL_AUTH = "universal-auth"
}
export interface IIdentity extends Document {
_id: Types.ObjectId;
name: string;
authMethod?: IdentityAuthMethod;
}
const identitySchema = new Schema(
{
name: {
type: String,
required: true
},
authMethod: {
type: String,
enum: IdentityAuthMethod,
required: false,
},
},
{
timestamps: true
}
);
export const Identity = model<IIdentity>("Identity", identitySchema);

View File

@@ -1,100 +0,0 @@
import { Document, Schema, Types, model } from "mongoose";
import { IIdentityTrustedIp } from "./identity";
export interface IIdentityAccessToken extends Document {
_id: Types.ObjectId;
identity: Types.ObjectId;
identityUniversalAuthClientSecret?: Types.ObjectId;
accessTokenLastUsedAt?: Date;
accessTokenLastRenewedAt?: Date;
accessTokenNumUses: number;
accessTokenNumUsesLimit: number;
accessTokenTTL: number;
accessTokenMaxTTL: number;
accessTokenTrustedIps: Array<IIdentityTrustedIp>;
isAccessTokenRevoked: boolean;
updatedAt: Date;
createdAt: Date;
}
const identityAccessTokenSchema = new Schema(
{
identity: {
type: Schema.Types.ObjectId,
ref: "Identity",
required: false,
},
identityUniversalAuthClientSecret: {
type: Schema.Types.ObjectId,
ref: "IdentityUniversalAuthClientSecret",
required: false,
},
accessTokenLastUsedAt: {
type: Date,
required: false,
},
accessTokenLastRenewedAt: {
type: Date,
required: false,
},
accessTokenNumUses: {
// number of times access token has been used
type: Number,
default: 0,
required: true,
},
accessTokenNumUsesLimit: {
// number of times access token can be used for
type: Number,
default: 0, // default: used as many times as needed
required: true,
},
accessTokenTTL: {
// seconds
// incremental lifetime
type: Number,
default: 2592000, // 30 days
required: true,
},
accessTokenMaxTTL: {
// seconds
// max lifetime
type: Number,
default: 2592000, // 30 days
required: true,
},
accessTokenTrustedIps: {
type: [
{
ipAddress: {
type: String,
required: true,
},
type: {
type: String,
required: true,
},
prefix: {
type: Number,
required: false,
},
},
],
default: [],
required: true,
},
isAccessTokenRevoked: {
type: Boolean,
default: false,
required: true,
},
},
{
timestamps: true,
},
);
export const IdentityAccessToken = model<IIdentityAccessToken>(
"IdentityAccessToken",
identityAccessTokenSchema,
);

View File

@@ -1,40 +0,0 @@
import { Schema, Types, model } from "mongoose";
export interface IIdentityMembership {
_id: Types.ObjectId;
identity: Types.ObjectId;
workspace: Types.ObjectId;
role: "admin" | "member" | "viewer" | "no-access" | "custom";
customRole: Types.ObjectId;
}
const identityMembershipSchema = new Schema<IIdentityMembership>(
{
identity: {
type: Schema.Types.ObjectId,
ref: "Identity",
},
workspace: {
type: Schema.Types.ObjectId,
ref: "Workspace",
required: true,
index: true,
},
role: {
type: String,
required: true,
},
customRole: {
type: Schema.Types.ObjectId,
ref: "Role",
},
},
{
timestamps: true,
},
);
export const IdentityMembership = model<IIdentityMembership>(
"IdentityMembership",
identityMembershipSchema,
);

View File

@@ -1,38 +0,0 @@
import { Schema, Types, model } from "mongoose";
export interface IIdentityMembershipOrg {
_id: Types.ObjectId;
identity: Types.ObjectId;
organization: Types.ObjectId;
role: "admin" | "member" | "no-access" | "custom";
customRole: Types.ObjectId;
}
const identityMembershipOrgSchema = new Schema<IIdentityMembershipOrg>(
{
identity: {
type: Schema.Types.ObjectId,
ref: "Identity",
},
organization: {
type: Schema.Types.ObjectId,
ref: "Organization",
},
role: {
type: String,
required: true,
},
customRole: {
type: Schema.Types.ObjectId,
ref: "Role",
},
},
{
timestamps: true,
},
);
export const IdentityMembershipOrg = model<IIdentityMembershipOrg>(
"IdentityMembershipOrg",
identityMembershipOrgSchema,
);

View File

@@ -1,99 +0,0 @@
import { Document, Schema, Types, model } from "mongoose";
export interface IIdentityUniversalAuth extends Document {
_id: Types.ObjectId;
identity: Types.ObjectId;
clientId: string;
clientSecretTrustedIps: Array<{}>;
accessTokenTTL: number;
accessTokenMaxTTL: number;
accessTokenNumUsesLimit: number;
accessTokenTrustedIps: Array<{}>;
}
const identityUniversalAuthSchema = new Schema(
{
identity: {
type: Schema.Types.ObjectId,
ref: "Identity",
required: true,
},
clientId: {
type: String,
required: true,
},
clientSecretTrustedIps: {
type: [
{
ipAddress: {
type: String,
required: true,
},
type: {
type: String,
required: true,
},
prefix: {
type: Number,
required: false,
},
},
],
default: [
{
ipAddress: "0.0.0.0",
prefix: 0,
},
],
required: true,
},
accessTokenTTL: {
// seconds
// incremental lifetime
type: Number,
default: 7200,
required: true,
},
accessTokenMaxTTL: {
// seconds
// max lifetime
type: Number,
default: 7200,
required: true,
},
accessTokenNumUsesLimit: {
// number of times access token can be used for
type: Number,
default: 0, // default: used as many times as needed
required: true,
},
accessTokenTrustedIps: {
type: [
{
ipAddress: {
type: String,
required: true,
},
type: {
type: String,
required: true,
},
prefix: {
type: Number,
required: false,
},
},
],
default: [],
required: true,
},
},
{
timestamps: true,
},
);
export const IdentityUniversalAuth = model<IIdentityUniversalAuth>(
"IdentityUniversalAuth",
identityUniversalAuthSchema,
);

View File

@@ -1,81 +0,0 @@
import { Document, Schema, Types, model } from "mongoose";
export interface IIdentityUniversalAuthClientSecret extends Document {
_id: Types.ObjectId;
identity: Types.ObjectId;
identityUniversalAuth : Types.ObjectId;
description: string;
clientSecretPrefix: string;
clientSecretHash: string;
clientSecretLastUsedAt?: Date;
clientSecretNumUses: number;
clientSecretNumUsesLimit: number;
clientSecretTTL: number;
updatedAt: Date;
createdAt: Date;
isClientSecretRevoked: boolean;
}
const identityUniversalAuthClientSecretSchema = new Schema(
{
identity: {
type: Schema.Types.ObjectId,
ref: "Identity",
required: true
},
identityUniversalAuth: {
type: Schema.Types.ObjectId,
ref: "IdentityUniversalAuth",
required: true
},
description: {
type: String,
required: true
},
clientSecretPrefix: {
type: String,
required: true
},
clientSecretHash: {
type: String,
required: true
},
clientSecretLastUsedAt: {
type: Date,
required: false
},
clientSecretNumUses: {
// number of times client secret has been used
// in login operation
type: Number,
default: 0,
required: true
},
clientSecretNumUsesLimit: {
// number of times client secret can be used for
// a login operation
type: Number,
default: 0, // default: used as many times as needed
required: true
},
clientSecretTTL: {
type: Number,
default: 0, // default: does not expire
required: true
},
isClientSecretRevoked: {
type: Boolean,
default: false,
required: true
}
},
{
timestamps: true
}
);
identityUniversalAuthClientSecretSchema.index(
{ identityUniversalAuth: 1, isClientSecretRevoked: 1 }
);
export const IdentityUniversalAuthClientSecret = model<IIdentityUniversalAuthClientSecret>("IdentityUniversalAuthClientSecret", identityUniversalAuthClientSecretSchema);

View File

@@ -1,29 +0,0 @@
import { Schema, Types, model } from "mongoose";
export interface IIncidentContactOrg {
_id: Types.ObjectId;
email: string;
organization: Types.ObjectId;
}
const incidentContactOrgSchema = new Schema<IIncidentContactOrg>(
{
email: {
type: String,
required: true,
},
organization: {
type: Schema.Types.ObjectId,
ref: "Organization",
required: true,
},
},
{
timestamps: true,
}
);
export const IncidentContactOrg = model<IIncidentContactOrg>(
"IncidentContactOrg",
incidentContactOrgSchema
);

View File

@@ -1,49 +0,0 @@
export * from "./backupPrivateKey";
export * from "./bot";
export * from "./botOrg";
export * from "./botKey";
export * from "./incidentContactOrg";
export * from "./integration/integration";
export * from "./integrationAuth";
export * from "./key";
export * from "./membership";
export * from "./membershipOrg";
export * from "./organization";
export * from "./secret";
export * from "./tag";
export * from "./folder";
export * from "./secretImports";
export * from "./secretBlindIndexData";
export * from "./serviceToken"; // TODO: deprecate
export * from "./tokenData";
export * from "./user";
export * from "./userAction";
export * from "./workspace";
export * from "./serviceTokenData"; // TODO: deprecate
// new
export * from "./identity";
export * from "./identityMembership";
export * from "./identityMembershipOrg";
export * from "./identityUniversalAuth";
export * from "./identityUniversalAuthClientSecret";
export * from "./identityAccessToken";
export * from "./apiKeyData"; // TODO: deprecate
export * from "./apiKeyDataV2";
export * from "./loginSRPDetail";
export * from "./tokenVersion";
export * from "./webhooks";
export * from "./secretSnapshot";
export * from "./secretVersion";
export * from "./folderVersion";
export * from "./role";
export * from "./ssoConfig";
export * from "./trustedIp";
export * from "./auditLog";
export * from "./gitRisks";
export * from "./gitAppOrganizationInstallation";
export * from "./gitAppInstallationSession";
export * from "./secretApprovalPolicy";
export * from "./secretApprovalRequest";

View File

@@ -1 +0,0 @@
export * from "./integration";

View File

@@ -1,155 +0,0 @@
import { Schema, Types, model } from "mongoose";
import { Metadata } from "./types";
export interface IIntegration {
_id: Types.ObjectId;
workspace: Types.ObjectId;
environment: string;
isActive: boolean;
url: string;
app: string;
appId: string;
owner: string;
targetEnvironment: string;
targetEnvironmentId: string;
targetService: string;
targetServiceId: string;
path: string;
region: string;
scope: string;
secretPath: string;
integration:
| "azure-key-vault"
| "aws-parameter-store"
| "aws-secret-manager"
| "heroku"
| "vercel"
| "netlify"
| "github"
| "gitlab"
| "render"
| "railway"
| "flyio"
| "circleci"
| "laravel-forge"
| "travisci"
| "supabase"
| "checkly"
| "qovery"
| "terraform-cloud"
| "teamcity"
| "hashicorp-vault"
| "cloudflare-pages"
| "cloudflare-workers"
| "bitbucket"
| "codefresh"
| "digital-ocean-app-platform"
| "cloud-66"
| "northflank"
| "windmill"
| "gcp-secret-manager"
| "hasura-cloud";
integrationAuth: Types.ObjectId;
metadata: Metadata;
}
const integrationSchema = new Schema<IIntegration>(
{
workspace: {
type: Schema.Types.ObjectId,
ref: "Workspace",
required: true,
},
environment: {
type: String,
required: true,
},
isActive: {
type: Boolean,
required: true,
},
url: {
// for custom self-hosted integrations (e.g. self-hosted GitHub enterprise)
type: String,
default: null,
},
app: {
// name of app in provider
type: String,
default: null,
},
appId: {
// id of app in provider
type: String,
default: null,
},
targetEnvironment: {
// target environment
type: String,
default: null,
},
targetEnvironmentId: {
type: String,
default: null,
},
targetService: {
// railway-specific service
// qovery-specific project
type: String,
default: null,
},
targetServiceId: {
// railway-specific service
// qovery specific project
type: String,
default: null,
},
owner: {
// github-specific repo owner-login
type: String,
default: null,
},
path: {
// aws-parameter-store-specific path
// (also) vercel preview-branch
type: String,
default: null,
},
region: {
// aws-parameter-store-specific path
type: String,
default: null,
},
scope: {
// qovery-specific scope
type: String,
default: null,
},
integration: {
type: String,
required: true,
},
integrationAuth: {
type: Schema.Types.ObjectId,
ref: "IntegrationAuth",
required: true,
},
secretPath: {
type: String,
required: true,
default: "/",
},
metadata: {
type: Schema.Types.Mixed,
default: {},
},
},
{
timestamps: true,
},
);
export const Integration = model<IIntegration>(
"Integration",
integrationSchema,
);

View File

@@ -1,13 +0,0 @@
export type Metadata = {
secretPrefix?: string;
secretSuffix?: string;
secretGCPLabel?: {
labelName: string;
labelValue: string;
}
secretAWSTag?: {
key: string;
value: string;
}[]
kmsKeyId?: string;
}

View File

@@ -1 +0,0 @@
export * from "./integrationAuth";

View File

@@ -1,144 +0,0 @@
import { Document, Schema, Types, model } from "mongoose";
import { IntegrationAuthMetadata } from "./types";
export interface IIntegrationAuth extends Document {
_id: Types.ObjectId;
workspace: Types.ObjectId;
integration:
| "heroku"
| "vercel"
| "netlify"
| "github"
| "gitlab"
| "render"
| "railway"
| "flyio"
| "azure-key-vault"
| "laravel-forge"
| "circleci"
| "travisci"
| "supabase"
| "aws-parameter-store"
| "aws-secret-manager"
| "checkly"
| "qovery"
| "cloudflare-pages"
| "cloudflare-workers"
| "codefresh"
| "digital-ocean-app-platform"
| "bitbucket"
| "cloud-66"
| "terraform-cloud"
| "teamcity"
| "northflank"
| "windmill"
| "gcp-secret-manager"
| "hasura-cloud";
teamId: string;
accountId: string;
url: string;
namespace: string;
refreshCiphertext?: string;
refreshIV?: string;
refreshTag?: string;
accessIdCiphertext?: string;
accessIdIV?: string;
accessIdTag?: string;
accessCiphertext?: string;
accessIV?: string;
accessTag?: string;
algorithm?: "aes-256-gcm";
keyEncoding?: "utf8" | "base64";
accessExpiresAt?: Date;
metadata?: IntegrationAuthMetadata;
}
const integrationAuthSchema = new Schema<IIntegrationAuth>(
{
workspace: {
type: Schema.Types.ObjectId,
ref: "Workspace",
required: true,
},
integration: {
type: String,
required: true,
},
teamId: {
// vercel-specific integration param
type: String,
},
url: {
// for any self-hosted integrations (e.g. self-hosted hashicorp-vault)
type: String,
},
namespace: {
// hashicorp-vault-specific integration param
type: String,
},
accountId: {
// netlify-specific integration param
type: String,
},
refreshCiphertext: {
type: String,
},
refreshIV: {
type: String,
},
refreshTag: {
type: String,
},
accessIdCiphertext: {
type: String,
},
accessIdIV: {
type: String,
},
accessIdTag: {
type: String,
},
accessCiphertext: {
type: String,
},
accessIV: {
type: String,
},
accessTag: {
type: String,
},
accessExpiresAt: {
type: Date,
},
algorithm: {
// the encryption algorithm used
type: String,
required: true,
},
keyEncoding: {
type: String,
required: true,
},
metadata: {
type: Schema.Types.Mixed,
},
},
{
timestamps: true,
},
);
export const IntegrationAuth = model<IIntegrationAuth>(
"IntegrationAuth",
integrationAuthSchema,
);

View File

@@ -1,5 +0,0 @@
interface GCPIntegrationAuthMetadata {
authMethod: "oauth2" | "serviceAccount"
}
export type IntegrationAuthMetadata = GCPIntegrationAuthMetadata;

View File

@@ -1,43 +0,0 @@
import { Schema, Types, model } from "mongoose";
export interface IKey {
_id: Types.ObjectId;
encryptedKey: string;
nonce: string;
sender: Types.ObjectId;
receiver: Types.ObjectId;
workspace: Types.ObjectId;
}
const keySchema = new Schema<IKey>(
{
encryptedKey: {
type: String,
required: true,
},
nonce: {
type: String,
required: true,
},
sender: {
type: Schema.Types.ObjectId,
ref: "User",
required: true,
},
receiver: {
type: Schema.Types.ObjectId,
ref: "User",
required: true,
},
workspace: {
type: Schema.Types.ObjectId,
ref: "Workspace",
required: true,
},
},
{
timestamps: true,
},
);
export const Key = model<IKey>("Key", keySchema);

View File

@@ -1,27 +0,0 @@
import mongoose, { Schema, Types, model } from "mongoose";
export interface ILoginSRPDetail {
_id: Types.ObjectId;
clientPublicKey: string;
email: string;
serverBInt: mongoose.Schema.Types.Buffer;
userId: string;
expireAt: Date;
}
const loginSRPDetailSchema = new Schema<ILoginSRPDetail>(
{
clientPublicKey: {
type: String,
required: true,
},
email: {
type: String,
unique: true,
},
serverBInt: { type: mongoose.Schema.Types.Buffer },
expireAt: { type: Date },
}
);
export const LoginSRPDetail = model("LoginSRPDetail", loginSRPDetailSchema);

View File

@@ -1,58 +0,0 @@
import { Schema, Types, model } from "mongoose";
export interface IMembershipPermission {
environmentSlug: string;
ability: string;
}
export interface IMembership {
_id: Types.ObjectId;
user: Types.ObjectId;
inviteEmail?: string;
workspace: Types.ObjectId;
role: "admin" | "member" | "viewer" | "no-access" | "custom";
customRole: Types.ObjectId;
deniedPermissions: IMembershipPermission[];
}
const membershipSchema = new Schema<IMembership>(
{
user: {
type: Schema.Types.ObjectId,
ref: "User",
},
inviteEmail: {
type: String,
},
workspace: {
type: Schema.Types.ObjectId,
ref: "Workspace",
required: true,
},
deniedPermissions: {
type: [
{
environmentSlug: String,
ability: {
type: String,
enum: ["read", "write"],
},
},
],
default: [],
},
role: {
type: String,
required: true,
},
customRole: {
type: Schema.Types.ObjectId,
ref: "Role",
},
},
{
timestamps: true,
},
);
export const Membership = model<IMembership>("Membership", membershipSchema);

View File

@@ -1,49 +0,0 @@
import { Document, Schema, Types, model } from "mongoose";
export interface IMembershipOrg extends Document {
_id: Types.ObjectId;
user: Types.ObjectId;
inviteEmail: string;
organization: Types.ObjectId;
role: "admin" | "member" | "no-access" | "custom";
customRole: Types.ObjectId;
status: "invited" | "accepted";
createdAt: string;
updatedAt: string;
}
const membershipOrgSchema = new Schema(
{
user: {
type: Schema.Types.ObjectId,
ref: "User",
},
inviteEmail: {
type: String,
},
organization: {
type: Schema.Types.ObjectId,
ref: "Organization",
},
role: {
type: String,
required: true,
},
status: {
type: String,
required: true,
},
customRole: {
type: Schema.Types.ObjectId,
ref: "Role",
},
},
{
timestamps: true,
},
);
export const MembershipOrg = model<IMembershipOrg>(
"MembershipOrg",
membershipOrgSchema,
);

Some files were not shown because too many files have changed in this diff Show More