mirror of
https://github.com/Infisical/infisical.git
synced 2025-04-11 16:58:11 +00:00
Compare commits
115 Commits
dynamic-1
...
infisical/
Author | SHA1 | Date | |
---|---|---|---|
593bdf74b8 | |||
1f3742e619 | |||
d6e5ac2133 | |||
fea48518a3 | |||
94d509eb01 | |||
ba1f8f4564 | |||
e26df005c2 | |||
aca9b47f82 | |||
a16ce8899b | |||
b61511d100 | |||
a945bdfc4c | |||
3f6999b2e3 | |||
9128461409 | |||
893235c40f | |||
d3cdaa8449 | |||
e0f655ae30 | |||
93aeca3a38 | |||
1edebdf8a5 | |||
1017707642 | |||
5639306303 | |||
72f50ec399 | |||
effc7a3627 | |||
510c91cef1 | |||
9be5d89fcf | |||
94f4497903 | |||
b5af5646ee | |||
1554618167 | |||
5fbfcdda30 | |||
cdbb3b9c47 | |||
0042a95b21 | |||
53233e05d4 | |||
4f15f9c8d3 | |||
97223fabe6 | |||
04b312cbe4 | |||
97e5069cf5 | |||
93146fcd96 | |||
87d98de4c1 | |||
26f647b948 | |||
80b3cdd128 | |||
8dd85a0d65 | |||
17995d301a | |||
094b48a2b1 | |||
7b8bfe38f0 | |||
9903f7c4a0 | |||
42cd98d4d9 | |||
4b203e9ad3 | |||
36bf1b2abc | |||
42fb732955 | |||
da2dcb347a | |||
b9482966cf | |||
1e4b4591ed | |||
4a325d6d96 | |||
5e20573110 | |||
f623c8159d | |||
4323407da7 | |||
4c496d5e3d | |||
d68dc4c3e0 | |||
e64c579dfd | |||
d0c0d5835c | |||
af2dcdd0c7 | |||
6c628a7265 | |||
00f2d40803 | |||
0a66cbe729 | |||
7fec7c9bf5 | |||
d1afec4f9a | |||
31ad6b0c86 | |||
e46256f45b | |||
64e868a151 | |||
c8cbcaf10c | |||
51716336c2 | |||
6b51c7269a | |||
f551a4158d | |||
e850b82fb3 | |||
8f85f292db | |||
5f84de039f | |||
8529fac098 | |||
81cf19cb4a | |||
edbe1c8eae | |||
a5039494cd | |||
a908471e66 | |||
84204c3c37 | |||
4931e8579c | |||
20dc243fd9 | |||
785a1389d9 | |||
5a3fc3568a | |||
497601e398 | |||
8db019d2fe | |||
07d1d91110 | |||
bb506fff9f | |||
7a561bcbdf | |||
8784f80fc1 | |||
0793e70c26 | |||
99f8799ff4 | |||
3f05c8b7ae | |||
6bd624a0f6 | |||
4a11096ea8 | |||
1589eb8e03 | |||
b370d6e415 | |||
65937d6a17 | |||
d20bc1b38a | |||
882ad8729c | |||
75d9463ceb | |||
4f05e4ce93 | |||
2e8680c5d4 | |||
e5136c9ef5 | |||
812fe5cf31 | |||
50082e192c | |||
1e1b5d655e | |||
3befd90723 | |||
88549f4030 | |||
46a638cc63 | |||
566f7e4c61 | |||
9ff3210ed6 | |||
f91a6683c2 | |||
c29cb667d7 |
@ -103,11 +103,15 @@ export const ${dalName} = (db: TDbClient) => {
|
||||
`import { z } from "zod";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { readLimit } from "@app/server/config/rateLimiter";
|
||||
|
||||
export const register${pascalCase}Router = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "GET",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({}),
|
||||
response: {
|
||||
|
@ -7,10 +7,10 @@ const prompt = promptSync({ sigint: true });
|
||||
|
||||
const migrationName = prompt("Enter name for migration: ");
|
||||
|
||||
// Remove spaces from migration name and replace with hyphens
|
||||
const formattedMigrationName = migrationName.replace(/\s+/g, "-");
|
||||
|
||||
execSync(
|
||||
`npx knex migrate:make --knexfile ${path.join(
|
||||
__dirname,
|
||||
"../src/db/knexfile.ts"
|
||||
)} -x ts ${migrationName}`,
|
||||
`npx knex migrate:make --knexfile ${path.join(__dirname, "../src/db/knexfile.ts")} -x ts ${formattedMigrationName}`,
|
||||
{ stdio: "inherit" }
|
||||
);
|
||||
|
8
backend/src/@types/fastify.d.ts
vendored
8
backend/src/@types/fastify.d.ts
vendored
@ -3,9 +3,13 @@ import "fastify";
|
||||
import { TUsers } from "@app/db/schemas";
|
||||
import { TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-service";
|
||||
import { TCreateAuditLogDTO } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { TDynamicSecretServiceFactory } from "@app/ee/services/dynamic-secret/dynamic-secret-service";
|
||||
import { TDynamicSecretLeaseServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-service";
|
||||
import { TIdentityProjectAdditionalPrivilegeServiceFactory } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service";
|
||||
import { TLdapConfigServiceFactory } from "@app/ee/services/ldap-config/ldap-config-service";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { TProjectUserAdditionalPrivilegeServiceFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-service";
|
||||
import { TSamlConfigServiceFactory } from "@app/ee/services/saml-config/saml-config-service";
|
||||
import { TScimServiceFactory } from "@app/ee/services/scim/scim-service";
|
||||
import { TSecretApprovalPolicyServiceFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-service";
|
||||
@ -21,8 +25,6 @@ import { TAuthPasswordFactory } from "@app/services/auth/auth-password-service";
|
||||
import { TAuthSignupFactory } from "@app/services/auth/auth-signup-service";
|
||||
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
|
||||
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
||||
import { TDynamicSecretServiceFactory } from "@app/services/dynamic-secret/dynamic-secret-service";
|
||||
import { TDynamicSecretLeaseServiceFactory } from "@app/services/dynamic-secret-lease/dynamic-secret-lease-service";
|
||||
import { TIdentityServiceFactory } from "@app/services/identity/identity-service";
|
||||
import { TIdentityAccessTokenServiceFactory } from "@app/services/identity-access-token/identity-access-token-service";
|
||||
import { TIdentityProjectServiceFactory } from "@app/services/identity-project/identity-project-service";
|
||||
@ -121,6 +123,8 @@ declare module "fastify" {
|
||||
telemetry: TTelemetryServiceFactory;
|
||||
dynamicSecret: TDynamicSecretServiceFactory;
|
||||
dynamicSecretLease: TDynamicSecretLeaseServiceFactory;
|
||||
projectUserAdditionalPrivilege: TProjectUserAdditionalPrivilegeServiceFactory;
|
||||
identityProjectAdditionalPrivilege: TIdentityProjectAdditionalPrivilegeServiceFactory;
|
||||
};
|
||||
// this is exclusive use for middlewares in which we need to inject data
|
||||
// everywhere else access using service layer
|
||||
|
16
backend/src/@types/knex.d.ts
vendored
16
backend/src/@types/knex.d.ts
vendored
@ -38,6 +38,9 @@ import {
|
||||
TIdentityOrgMemberships,
|
||||
TIdentityOrgMembershipsInsert,
|
||||
TIdentityOrgMembershipsUpdate,
|
||||
TIdentityProjectAdditionalPrivilege,
|
||||
TIdentityProjectAdditionalPrivilegeInsert,
|
||||
TIdentityProjectAdditionalPrivilegeUpdate,
|
||||
TIdentityProjectMembershipRole,
|
||||
TIdentityProjectMembershipRoleInsert,
|
||||
TIdentityProjectMembershipRoleUpdate,
|
||||
@ -92,6 +95,9 @@ import {
|
||||
TProjects,
|
||||
TProjectsInsert,
|
||||
TProjectsUpdate,
|
||||
TProjectUserAdditionalPrivilege,
|
||||
TProjectUserAdditionalPrivilegeInsert,
|
||||
TProjectUserAdditionalPrivilegeUpdate,
|
||||
TProjectUserMembershipRoles,
|
||||
TProjectUserMembershipRolesInsert,
|
||||
TProjectUserMembershipRolesUpdate,
|
||||
@ -239,6 +245,11 @@ declare module "knex/types/tables" {
|
||||
TProjectUserMembershipRolesUpdate
|
||||
>;
|
||||
[TableName.ProjectRoles]: Knex.CompositeTableType<TProjectRoles, TProjectRolesInsert, TProjectRolesUpdate>;
|
||||
[TableName.ProjectUserAdditionalPrivilege]: Knex.CompositeTableType<
|
||||
TProjectUserAdditionalPrivilege,
|
||||
TProjectUserAdditionalPrivilegeInsert,
|
||||
TProjectUserAdditionalPrivilegeUpdate
|
||||
>;
|
||||
[TableName.ProjectKeys]: Knex.CompositeTableType<TProjectKeys, TProjectKeysInsert, TProjectKeysUpdate>;
|
||||
[TableName.Secret]: Knex.CompositeTableType<TSecrets, TSecretsInsert, TSecretsUpdate>;
|
||||
[TableName.SecretBlindIndex]: Knex.CompositeTableType<
|
||||
@ -294,6 +305,11 @@ declare module "knex/types/tables" {
|
||||
TIdentityProjectMembershipRoleInsert,
|
||||
TIdentityProjectMembershipRoleUpdate
|
||||
>;
|
||||
[TableName.IdentityProjectAdditionalPrivilege]: Knex.CompositeTableType<
|
||||
TIdentityProjectAdditionalPrivilege,
|
||||
TIdentityProjectAdditionalPrivilegeInsert,
|
||||
TIdentityProjectAdditionalPrivilegeUpdate
|
||||
>;
|
||||
[TableName.ScimToken]: Knex.CompositeTableType<TScimTokens, TScimTokensInsert, TScimTokensUpdate>;
|
||||
[TableName.SecretApprovalPolicy]: Knex.CompositeTableType<
|
||||
TSecretApprovalPolicies,
|
||||
|
@ -0,0 +1,29 @@
|
||||
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.ProjectUserAdditionalPrivilege))) {
|
||||
await knex.schema.createTable(TableName.ProjectUserAdditionalPrivilege, (t) => {
|
||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||
t.string("slug", 60).notNullable();
|
||||
t.uuid("projectMembershipId").notNullable();
|
||||
t.foreign("projectMembershipId").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
|
||||
t.boolean("isTemporary").notNullable().defaultTo(false);
|
||||
t.string("temporaryMode");
|
||||
t.string("temporaryRange"); // could be cron or relative time like 1H or 1minute etc
|
||||
t.datetime("temporaryAccessStartTime");
|
||||
t.datetime("temporaryAccessEndTime");
|
||||
t.jsonb("permissions").notNullable();
|
||||
t.timestamps(true, true, true);
|
||||
});
|
||||
}
|
||||
|
||||
await createOnUpdateTrigger(knex, TableName.ProjectUserAdditionalPrivilege);
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
await dropOnUpdateTrigger(knex, TableName.ProjectUserAdditionalPrivilege);
|
||||
await knex.schema.dropTableIfExists(TableName.ProjectUserAdditionalPrivilege);
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
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.IdentityProjectAdditionalPrivilege))) {
|
||||
await knex.schema.createTable(TableName.IdentityProjectAdditionalPrivilege, (t) => {
|
||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||
t.string("slug", 60).notNullable();
|
||||
t.uuid("projectMembershipId").notNullable();
|
||||
t.foreign("projectMembershipId")
|
||||
.references("id")
|
||||
.inTable(TableName.IdentityProjectMembership)
|
||||
.onDelete("CASCADE");
|
||||
t.boolean("isTemporary").notNullable().defaultTo(false);
|
||||
t.string("temporaryMode");
|
||||
t.string("temporaryRange"); // could be cron or relative time like 1H or 1minute etc
|
||||
t.datetime("temporaryAccessStartTime");
|
||||
t.datetime("temporaryAccessEndTime");
|
||||
t.jsonb("permissions").notNullable();
|
||||
t.timestamps(true, true, true);
|
||||
});
|
||||
}
|
||||
|
||||
await createOnUpdateTrigger(knex, TableName.IdentityProjectAdditionalPrivilege);
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
await dropOnUpdateTrigger(knex, TableName.IdentityProjectAdditionalPrivilege);
|
||||
await knex.schema.dropTableIfExists(TableName.IdentityProjectAdditionalPrivilege);
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
import { Knex } from "knex";
|
||||
import { z } from "zod";
|
||||
|
||||
import { TableName, TOrgMemberships } from "../schemas";
|
||||
|
||||
const validateOrgMembership = (membershipToValidate: TOrgMemberships, firstMembership: TOrgMemberships) => {
|
||||
const firstOrgId = firstMembership.orgId;
|
||||
const firstUserId = firstMembership.userId;
|
||||
|
||||
if (membershipToValidate.id === firstMembership.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (membershipToValidate.inviteEmail !== firstMembership.inviteEmail) {
|
||||
throw new Error(`Invite emails are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
|
||||
}
|
||||
if (membershipToValidate.orgId !== firstMembership.orgId) {
|
||||
throw new Error(`OrgIds are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
|
||||
}
|
||||
if (membershipToValidate.role !== firstMembership.role) {
|
||||
throw new Error(`Roles are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
|
||||
}
|
||||
if (membershipToValidate.roleId !== firstMembership.roleId) {
|
||||
throw new Error(`RoleIds are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
|
||||
}
|
||||
if (membershipToValidate.status !== firstMembership.status) {
|
||||
throw new Error(`Statuses are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
|
||||
}
|
||||
if (membershipToValidate.userId !== firstMembership.userId) {
|
||||
throw new Error(`UserIds are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
|
||||
}
|
||||
};
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
const RowSchema = z.object({
|
||||
userId: z.string(),
|
||||
orgId: z.string(),
|
||||
cnt: z.string()
|
||||
});
|
||||
|
||||
// Transactional find and delete duplicate rows
|
||||
await knex.transaction(async (tx) => {
|
||||
const duplicateRows = await tx(TableName.OrgMembership)
|
||||
.select("userId", "orgId") // Select the userId and orgId so we can group by them
|
||||
.count("* as cnt") // Count the number of rows for each userId and orgId, so we can make sure there are more than 1 row (a duplicate)
|
||||
.groupBy("userId", "orgId")
|
||||
.havingRaw("count(*) > ?", [1]); // Using havingRaw for direct SQL expressions
|
||||
|
||||
// Parse the rows to ensure they are in the correct format, and for type safety
|
||||
const parsedRows = RowSchema.array().parse(duplicateRows);
|
||||
|
||||
// For each of the duplicate rows, loop through and find the actual memberships to delete
|
||||
for (const row of parsedRows) {
|
||||
const count = Number(row.cnt);
|
||||
|
||||
// An extra check to ensure that the count is actually a number, and the number is greater than 2
|
||||
if (typeof count !== "number" || count < 2) {
|
||||
// eslint-disable-next-line no-continue
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find all the organization memberships that have the same userId and orgId
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const rowsToDelete = await tx(TableName.OrgMembership).where({
|
||||
userId: row.userId,
|
||||
orgId: row.orgId
|
||||
});
|
||||
|
||||
// Ensure that all the rows have exactly the same value, except id, createdAt, updatedAt
|
||||
for (const rowToDelete of rowsToDelete) {
|
||||
validateOrgMembership(rowToDelete, rowsToDelete[0]);
|
||||
}
|
||||
|
||||
// Find the row with the latest createdAt, which we will keep
|
||||
|
||||
let lowestCreatedAt: number | null = null;
|
||||
let latestCreatedRow: TOrgMemberships | null = null;
|
||||
|
||||
for (const rowToDelete of rowsToDelete) {
|
||||
if (lowestCreatedAt === null || rowToDelete.createdAt.getTime() < lowestCreatedAt) {
|
||||
lowestCreatedAt = rowToDelete.createdAt.getTime();
|
||||
latestCreatedRow = rowToDelete;
|
||||
}
|
||||
}
|
||||
if (!latestCreatedRow) {
|
||||
throw new Error("Failed to find last created membership");
|
||||
}
|
||||
|
||||
// Filter out the latest row from the rows to delete
|
||||
const membershipIdsToDelete = rowsToDelete.map((r) => r.id).filter((id) => id !== latestCreatedRow!.id);
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const numberOfRowsDeleted = await tx(TableName.OrgMembership).whereIn("id", membershipIdsToDelete).delete();
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
`Deleted ${numberOfRowsDeleted} duplicate organization memberships for ${row.userId} and ${row.orgId}`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
await knex.schema.alterTable(TableName.OrgMembership, (table) => {
|
||||
table.unique(["userId", "orgId"]);
|
||||
});
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
await knex.schema.alterTable(TableName.OrgMembership, (table) => {
|
||||
table.dropUnique(["userId", "orgId"]);
|
||||
});
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
// Code generated by automation script, DO NOT EDIT.
|
||||
// Automated by pulling database and generating zod schema
|
||||
// To update. Just run npm run generate:schema
|
||||
// Written by akhilmhdh.
|
||||
|
||||
import { z } from "zod";
|
||||
|
||||
import { TImmutableDBKeys } from "./models";
|
||||
|
||||
export const IdentityProjectAdditionalPrivilegeSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
slug: z.string(),
|
||||
projectMembershipId: z.string().uuid(),
|
||||
isTemporary: z.boolean().default(false),
|
||||
temporaryMode: z.string().nullable().optional(),
|
||||
temporaryRange: z.string().nullable().optional(),
|
||||
temporaryAccessStartTime: z.date().nullable().optional(),
|
||||
temporaryAccessEndTime: z.date().nullable().optional(),
|
||||
permissions: z.unknown(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date()
|
||||
});
|
||||
|
||||
export type TIdentityProjectAdditionalPrivilege = z.infer<typeof IdentityProjectAdditionalPrivilegeSchema>;
|
||||
export type TIdentityProjectAdditionalPrivilegeInsert = Omit<
|
||||
z.input<typeof IdentityProjectAdditionalPrivilegeSchema>,
|
||||
TImmutableDBKeys
|
||||
>;
|
||||
export type TIdentityProjectAdditionalPrivilegeUpdate = Partial<
|
||||
Omit<z.input<typeof IdentityProjectAdditionalPrivilegeSchema>, TImmutableDBKeys>
|
||||
>;
|
@ -10,6 +10,7 @@ export * from "./git-app-org";
|
||||
export * from "./identities";
|
||||
export * from "./identity-access-tokens";
|
||||
export * from "./identity-org-memberships";
|
||||
export * from "./identity-project-additional-privilege";
|
||||
export * from "./identity-project-membership-role";
|
||||
export * from "./identity-project-memberships";
|
||||
export * from "./identity-ua-client-secrets";
|
||||
@ -28,6 +29,7 @@ export * from "./project-environments";
|
||||
export * from "./project-keys";
|
||||
export * from "./project-memberships";
|
||||
export * from "./project-roles";
|
||||
export * from "./project-user-additional-privilege";
|
||||
export * from "./project-user-membership-roles";
|
||||
export * from "./projects";
|
||||
export * from "./saml-configs";
|
||||
|
@ -20,6 +20,7 @@ export enum TableName {
|
||||
Environment = "project_environments",
|
||||
ProjectMembership = "project_memberships",
|
||||
ProjectRoles = "project_roles",
|
||||
ProjectUserAdditionalPrivilege = "project_user_additional_privilege",
|
||||
ProjectUserMembershipRole = "project_user_membership_roles",
|
||||
ProjectKeys = "project_keys",
|
||||
Secret = "secrets",
|
||||
@ -43,6 +44,7 @@ export enum TableName {
|
||||
IdentityOrgMembership = "identity_org_memberships",
|
||||
IdentityProjectMembership = "identity_project_memberships",
|
||||
IdentityProjectMembershipRole = "identity_project_membership_role",
|
||||
IdentityProjectAdditionalPrivilege = "identity_project_additional_privilege",
|
||||
ScimToken = "scim_tokens",
|
||||
SecretApprovalPolicy = "secret_approval_policies",
|
||||
SecretApprovalPolicyApprover = "secret_approval_policies_approvers",
|
||||
|
31
backend/src/db/schemas/project-user-additional-privilege.ts
Normal file
31
backend/src/db/schemas/project-user-additional-privilege.ts
Normal file
@ -0,0 +1,31 @@
|
||||
// Code generated by automation script, DO NOT EDIT.
|
||||
// Automated by pulling database and generating zod schema
|
||||
// To update. Just run npm run generate:schema
|
||||
// Written by akhilmhdh.
|
||||
|
||||
import { z } from "zod";
|
||||
|
||||
import { TImmutableDBKeys } from "./models";
|
||||
|
||||
export const ProjectUserAdditionalPrivilegeSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
slug: z.string(),
|
||||
projectMembershipId: z.string().uuid(),
|
||||
isTemporary: z.boolean().default(false),
|
||||
temporaryMode: z.string().nullable().optional(),
|
||||
temporaryRange: z.string().nullable().optional(),
|
||||
temporaryAccessStartTime: z.date().nullable().optional(),
|
||||
temporaryAccessEndTime: z.date().nullable().optional(),
|
||||
permissions: z.unknown(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date()
|
||||
});
|
||||
|
||||
export type TProjectUserAdditionalPrivilege = z.infer<typeof ProjectUserAdditionalPrivilegeSchema>;
|
||||
export type TProjectUserAdditionalPrivilegeInsert = Omit<
|
||||
z.input<typeof ProjectUserAdditionalPrivilegeSchema>,
|
||||
TImmutableDBKeys
|
||||
>;
|
||||
export type TProjectUserAdditionalPrivilegeUpdate = Partial<
|
||||
Omit<z.input<typeof ProjectUserAdditionalPrivilegeSchema>, TImmutableDBKeys>
|
||||
>;
|
@ -5,15 +5,18 @@ import { DynamicSecretLeasesSchema } from "@app/db/schemas";
|
||||
import { DYNAMIC_SECRET_LEASES } from "@app/lib/api-docs";
|
||||
import { daysToMillisecond } from "@app/lib/dates";
|
||||
import { removeTrailingSlash } from "@app/lib/fn";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { SanitizedDynamicSecretSchema } from "@app/server/routes/sanitizedSchemas";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
import { SanitizedDynamicSecretSchema } from "../sanitizedSchemas";
|
||||
|
||||
export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "POST",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
dynamicSecretName: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.CREATE.dynamicSecretName).toLowerCase(),
|
||||
@ -56,8 +59,11 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:leaseId",
|
||||
method: "DELETE",
|
||||
url: "/:leaseId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
leaseId: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.DELETE.leaseId)
|
||||
@ -95,8 +101,11 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:leaseId/renew",
|
||||
method: "POST",
|
||||
url: "/:leaseId/renew",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
leaseId: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.RENEW.leaseId)
|
||||
@ -147,6 +156,9 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide
|
||||
server.route({
|
||||
url: "/:leaseId",
|
||||
method: "GET",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
leaseId: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.GET_BY_LEASEID.leaseId)
|
@ -3,19 +3,22 @@ import ms from "ms";
|
||||
import { z } from "zod";
|
||||
|
||||
import { DynamicSecretLeasesSchema } from "@app/db/schemas";
|
||||
import { DynamicSecretProviderSchema } from "@app/ee/services/dynamic-secret/providers/models";
|
||||
import { DYNAMIC_SECRETS } from "@app/lib/api-docs";
|
||||
import { daysToMillisecond } from "@app/lib/dates";
|
||||
import { removeTrailingSlash } from "@app/lib/fn";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { SanitizedDynamicSecretSchema } from "@app/server/routes/sanitizedSchemas";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { DynamicSecretProviderSchema } from "@app/services/dynamic-secret/providers/models";
|
||||
|
||||
import { SanitizedDynamicSecretSchema } from "../sanitizedSchemas";
|
||||
|
||||
export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "POST",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
projectSlug: z.string().min(1).describe(DYNAMIC_SECRETS.CREATE.projectSlug),
|
||||
@ -75,8 +78,11 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:name",
|
||||
method: "PATCH",
|
||||
url: "/:name",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
name: z.string().toLowerCase().describe(DYNAMIC_SECRETS.UPDATE.name)
|
||||
@ -139,8 +145,11 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:name",
|
||||
method: "DELETE",
|
||||
url: "/:name",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
name: z.string().toLowerCase().describe(DYNAMIC_SECRETS.DELETE.name)
|
||||
@ -174,6 +183,9 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
|
||||
server.route({
|
||||
url: "/:name",
|
||||
method: "GET",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
name: z.string().min(1).describe(DYNAMIC_SECRETS.GET_BY_NAME.name)
|
||||
@ -208,6 +220,9 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "GET",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
querystring: z.object({
|
||||
projectSlug: z.string().min(1).describe(DYNAMIC_SECRETS.LIST.projectSlug),
|
||||
@ -236,6 +251,9 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
|
||||
server.route({
|
||||
url: "/:name/leases",
|
||||
method: "GET",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
name: z.string().min(1).describe(DYNAMIC_SECRETS.LIST_LEAES_BY_NAME.name)
|
@ -0,0 +1,329 @@
|
||||
import { MongoAbility, RawRuleOf } from "@casl/ability";
|
||||
import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
import ms from "ms";
|
||||
import { z } from "zod";
|
||||
|
||||
import { IdentityProjectAdditionalPrivilegeSchema } from "@app/db/schemas";
|
||||
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-types";
|
||||
import { ProjectPermissionSet } from "@app/ee/services/permission/project-permission";
|
||||
import { IDENTITY_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/permanent",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Create a permanent or a non expiry specific privilege for identity.",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
body: z.object({
|
||||
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.identityId),
|
||||
projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.projectSlug),
|
||||
slug: z
|
||||
.string()
|
||||
.min(1)
|
||||
.max(60)
|
||||
.trim()
|
||||
.refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
||||
.refine((v) => slugify(v) === v, {
|
||||
message: "Slug must be a valid slug"
|
||||
})
|
||||
.optional()
|
||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.slug),
|
||||
permissions: z.any().array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.permissions)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
privilege: IdentityProjectAdditionalPrivilegeSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const privilege = await server.services.identityProjectAdditionalPrivilege.create({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
...req.body,
|
||||
slug: req.body.slug ? slugify(req.body.slug) : slugify(alphaNumericNanoId(12)),
|
||||
isTemporary: false,
|
||||
permissions: JSON.stringify(packRules(req.body.permissions))
|
||||
});
|
||||
return { privilege };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/temporary",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Create a temporary or a expiring specific privilege for identity.",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
body: z.object({
|
||||
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.identityId),
|
||||
projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.projectSlug),
|
||||
slug: z
|
||||
.string()
|
||||
.min(1)
|
||||
.max(60)
|
||||
.trim()
|
||||
.refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
||||
.refine((v) => slugify(v) === v, {
|
||||
message: "Slug must be a valid slug"
|
||||
})
|
||||
.optional()
|
||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.slug),
|
||||
permissions: z.any().array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.permissions),
|
||||
temporaryMode: z
|
||||
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
|
||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.temporaryMode),
|
||||
temporaryRange: z
|
||||
.string()
|
||||
.refine((val) => ms(val) > 0, "Temporary range must be a positive number")
|
||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.temporaryRange),
|
||||
temporaryAccessStartTime: z
|
||||
.string()
|
||||
.datetime()
|
||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.temporaryAccessStartTime)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
privilege: IdentityProjectAdditionalPrivilegeSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const privilege = await server.services.identityProjectAdditionalPrivilege.create({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
...req.body,
|
||||
slug: req.body.slug ? slugify(req.body.slug) : slugify(alphaNumericNanoId(12)),
|
||||
isTemporary: true,
|
||||
permissions: JSON.stringify(packRules(req.body.permissions))
|
||||
});
|
||||
return { privilege };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "PATCH",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Update a specific privilege of an identity.",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
body: z.object({
|
||||
// disallow empty string
|
||||
privilegeSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.slug),
|
||||
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.identityId),
|
||||
projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.projectSlug),
|
||||
privilegeDetails: z
|
||||
.object({
|
||||
slug: z
|
||||
.string()
|
||||
.min(1)
|
||||
.max(60)
|
||||
.trim()
|
||||
.refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
||||
.refine((v) => slugify(v) === v, {
|
||||
message: "Slug must be a valid slug"
|
||||
})
|
||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.newSlug),
|
||||
permissions: z.any().array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.permissions),
|
||||
isTemporary: z.boolean().describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.isTemporary),
|
||||
temporaryMode: z
|
||||
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
|
||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.temporaryMode),
|
||||
temporaryRange: z
|
||||
.string()
|
||||
.refine((val) => ms(val) > 0, "Temporary range must be a positive number")
|
||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.temporaryRange),
|
||||
temporaryAccessStartTime: z
|
||||
.string()
|
||||
.datetime()
|
||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.temporaryAccessStartTime)
|
||||
})
|
||||
.partial()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
privilege: IdentityProjectAdditionalPrivilegeSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const updatedInfo = req.body.privilegeDetails;
|
||||
const privilege = await server.services.identityProjectAdditionalPrivilege.updateBySlug({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
slug: req.body.privilegeSlug,
|
||||
identityId: req.body.identityId,
|
||||
projectSlug: req.body.projectSlug,
|
||||
data: {
|
||||
...updatedInfo,
|
||||
permissions: updatedInfo?.permissions ? JSON.stringify(packRules(updatedInfo.permissions)) : undefined
|
||||
}
|
||||
});
|
||||
return { privilege };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Delete a specific privilege of an identity.",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
body: z.object({
|
||||
privilegeSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.DELETE.slug),
|
||||
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.DELETE.identityId),
|
||||
projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.DELETE.projectSlug)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
privilege: IdentityProjectAdditionalPrivilegeSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const privilege = await server.services.identityProjectAdditionalPrivilege.deleteBySlug({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
slug: req.body.privilegeSlug,
|
||||
identityId: req.body.identityId,
|
||||
projectSlug: req.body.projectSlug
|
||||
});
|
||||
return { privilege };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:privilegeSlug",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Retrieve details of a specific privilege by privilege slug.",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
privilegeSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.GET_BY_SLUG.slug)
|
||||
}),
|
||||
querystring: z.object({
|
||||
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.GET_BY_SLUG.identityId),
|
||||
projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.GET_BY_SLUG.projectSlug)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
privilege: IdentityProjectAdditionalPrivilegeSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const privilege = await server.services.identityProjectAdditionalPrivilege.getPrivilegeDetailsBySlug({
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
slug: req.params.privilegeSlug,
|
||||
...req.query
|
||||
});
|
||||
return { privilege };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: "List of a specific privilege of an identity in a project.",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
querystring: z.object({
|
||||
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.identityId),
|
||||
projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.projectSlug),
|
||||
unpacked: z
|
||||
.enum(["false", "true"])
|
||||
.transform((el) => el === "true")
|
||||
.default("true")
|
||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.unpacked)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
privileges: IdentityProjectAdditionalPrivilegeSchema.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const privileges = await server.services.identityProjectAdditionalPrivilege.listIdentityProjectPrivileges({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.query
|
||||
});
|
||||
if (req.query.unpacked) {
|
||||
return {
|
||||
privileges: privileges.map(({ permissions, ...el }) => ({
|
||||
...el,
|
||||
permissions: unpackRules(permissions as PackRule<RawRuleOf<MongoAbility<ProjectPermissionSet>>>[])
|
||||
}))
|
||||
};
|
||||
}
|
||||
return { privileges };
|
||||
}
|
||||
});
|
||||
};
|
@ -1,3 +1,6 @@
|
||||
import { registerDynamicSecretLeaseRouter } from "./dynamic-secret-lease-router";
|
||||
import { registerDynamicSecretRouter } from "./dynamic-secret-router";
|
||||
import { registerIdentityProjectAdditionalPrivilegeRouter } from "./identity-project-additional-privilege-router";
|
||||
import { registerLdapRouter } from "./ldap-router";
|
||||
import { registerLicenseRouter } from "./license-router";
|
||||
import { registerOrgRoleRouter } from "./org-role-router";
|
||||
@ -13,6 +16,7 @@ import { registerSecretScanningRouter } from "./secret-scanning-router";
|
||||
import { registerSecretVersionRouter } from "./secret-version-router";
|
||||
import { registerSnapshotRouter } from "./snapshot-router";
|
||||
import { registerTrustedIpRouter } from "./trusted-ip-router";
|
||||
import { registerUserAdditionalPrivilegeRouter } from "./user-additional-privilege-router";
|
||||
|
||||
export const registerV1EERoutes = async (server: FastifyZodProvider) => {
|
||||
// org role starts with organization
|
||||
@ -34,10 +38,26 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => {
|
||||
await server.register(registerSecretRotationProviderRouter, {
|
||||
prefix: "/secret-rotation-providers"
|
||||
});
|
||||
|
||||
await server.register(
|
||||
async (dynamicSecretRouter) => {
|
||||
await dynamicSecretRouter.register(registerDynamicSecretRouter);
|
||||
await dynamicSecretRouter.register(registerDynamicSecretLeaseRouter, { prefix: "/leases" });
|
||||
},
|
||||
{ prefix: "/dynamic-secrets" }
|
||||
);
|
||||
|
||||
await server.register(registerSamlRouter, { prefix: "/sso" });
|
||||
await server.register(registerScimRouter, { prefix: "/scim" });
|
||||
await server.register(registerLdapRouter, { prefix: "/ldap" });
|
||||
await server.register(registerSecretScanningRouter, { prefix: "/secret-scanning" });
|
||||
await server.register(registerSecretRotationRouter, { prefix: "/secret-rotations" });
|
||||
await server.register(registerSecretVersionRouter, { prefix: "/secret" });
|
||||
await server.register(
|
||||
async (privilegeRouter) => {
|
||||
await privilegeRouter.register(registerUserAdditionalPrivilegeRouter, { prefix: "/users" });
|
||||
await privilegeRouter.register(registerIdentityProjectAdditionalPrivilegeRouter, { prefix: "/identity" });
|
||||
},
|
||||
{ prefix: "/additional-privilege" }
|
||||
);
|
||||
};
|
||||
|
@ -17,6 +17,7 @@ import { z } from "zod";
|
||||
import { LdapConfigsSchema } from "@app/db/schemas";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -97,8 +98,11 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/config",
|
||||
method: "GET",
|
||||
url: "/config",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
querystring: z.object({
|
||||
@ -130,8 +134,11 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/config",
|
||||
method: "POST",
|
||||
url: "/config",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
body: z.object({
|
||||
@ -164,6 +171,9 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/config",
|
||||
method: "PATCH",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
body: z
|
||||
|
@ -3,13 +3,17 @@
|
||||
// TODO(akhilmhdh): Fix this when licence service gets it type
|
||||
import { z } from "zod";
|
||||
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/:organizationId/plans/table",
|
||||
method: "GET",
|
||||
url: "/:organizationId/plans/table",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
querystring: z.object({ billingCycle: z.enum(["monthly", "yearly"]) }),
|
||||
params: z.object({ organizationId: z.string().trim() }),
|
||||
@ -32,8 +36,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:organizationId/plan",
|
||||
method: "GET",
|
||||
url: "/:organizationId/plan",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({ organizationId: z.string().trim() }),
|
||||
response: {
|
||||
@ -54,8 +61,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:organizationId/plans",
|
||||
method: "GET",
|
||||
url: "/:organizationId/plans",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({ organizationId: z.string().trim() }),
|
||||
querystring: z.object({ workspaceId: z.string().trim().optional() }),
|
||||
@ -77,8 +87,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:organizationId/session/trial",
|
||||
method: "POST",
|
||||
url: "/:organizationId/session/trial",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({ organizationId: z.string().trim() }),
|
||||
body: z.object({ success_url: z.string().trim() }),
|
||||
@ -103,6 +116,9 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/:organizationId/customer-portal-session",
|
||||
method: "POST",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({ organizationId: z.string().trim() }),
|
||||
response: {
|
||||
@ -123,8 +139,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:organizationId/plan/billing",
|
||||
method: "GET",
|
||||
url: "/:organizationId/plan/billing",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({ organizationId: z.string().trim() }),
|
||||
response: {
|
||||
@ -145,8 +164,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:organizationId/plan/table",
|
||||
method: "GET",
|
||||
url: "/:organizationId/plan/table",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({ organizationId: z.string().trim() }),
|
||||
response: {
|
||||
@ -167,8 +189,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:organizationId/billing-details",
|
||||
method: "GET",
|
||||
url: "/:organizationId/billing-details",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({ organizationId: z.string().trim() }),
|
||||
response: {
|
||||
@ -189,8 +214,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:organizationId/billing-details",
|
||||
method: "PATCH",
|
||||
url: "/:organizationId/billing-details",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({ organizationId: z.string().trim() }),
|
||||
body: z.object({
|
||||
@ -217,8 +245,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:organizationId/billing-details/payment-methods",
|
||||
method: "GET",
|
||||
url: "/:organizationId/billing-details/payment-methods",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({ organizationId: z.string().trim() }),
|
||||
response: {
|
||||
@ -239,8 +270,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:organizationId/billing-details/payment-methods",
|
||||
method: "POST",
|
||||
url: "/:organizationId/billing-details/payment-methods",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({ organizationId: z.string().trim() }),
|
||||
body: z.object({
|
||||
@ -267,8 +301,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:organizationId/billing-details/payment-methods/:pmtMethodId",
|
||||
method: "DELETE",
|
||||
url: "/:organizationId/billing-details/payment-methods/:pmtMethodId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
organizationId: z.string().trim(),
|
||||
@ -293,8 +330,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:organizationId/billing-details/tax-ids",
|
||||
method: "GET",
|
||||
url: "/:organizationId/billing-details/tax-ids",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
organizationId: z.string().trim()
|
||||
@ -317,8 +357,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:organizationId/billing-details/tax-ids",
|
||||
method: "POST",
|
||||
url: "/:organizationId/billing-details/tax-ids",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
organizationId: z.string().trim()
|
||||
@ -347,8 +390,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:organizationId/billing-details/tax-ids/:taxId",
|
||||
method: "DELETE",
|
||||
url: "/:organizationId/billing-details/tax-ids/:taxId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
organizationId: z.string().trim(),
|
||||
@ -373,8 +419,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:organizationId/invoices",
|
||||
method: "GET",
|
||||
url: "/:organizationId/invoices",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
organizationId: z.string().trim()
|
||||
@ -397,8 +446,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:organizationId/licenses",
|
||||
method: "GET",
|
||||
url: "/:organizationId/licenses",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
organizationId: z.string().trim()
|
||||
|
@ -2,6 +2,7 @@ import slugify from "@sindresorhus/slugify";
|
||||
import { z } from "zod";
|
||||
|
||||
import { OrgMembershipRole, OrgMembershipsSchema, OrgRolesSchema } from "@app/db/schemas";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -9,6 +10,9 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/:organizationId/roles",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
organizationId: z.string().trim()
|
||||
@ -51,6 +55,9 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "PATCH",
|
||||
url: "/:organizationId/roles/:roleId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
organizationId: z.string().trim(),
|
||||
@ -95,6 +102,9 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/:organizationId/roles/:roleId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
organizationId: z.string().trim(),
|
||||
@ -122,6 +132,9 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:organizationId/roles",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
organizationId: z.string().trim()
|
||||
@ -151,6 +164,9 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:organizationId/permissions",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
organizationId: z.string().trim()
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { ProjectMembershipsSchema, ProjectRolesSchema } from "@app/db/schemas";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -8,6 +9,9 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/:projectId/roles",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
projectId: z.string().trim()
|
||||
@ -41,6 +45,9 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "PATCH",
|
||||
url: "/:projectId/roles/:roleId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
projectId: z.string().trim(),
|
||||
@ -76,6 +83,9 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/:projectId/roles/:roleId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
projectId: z.string().trim(),
|
||||
@ -104,6 +114,9 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:projectId/roles",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
projectId: z.string().trim()
|
||||
@ -134,6 +147,9 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:projectId/permissions",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
projectId: z.string().trim()
|
||||
|
@ -4,6 +4,7 @@ import { AuditLogsSchema, SecretSnapshotsSchema } from "@app/db/schemas";
|
||||
import { EventType, UserAgentType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { AUDIT_LOGS, PROJECTS } from "@app/lib/api-docs";
|
||||
import { removeTrailingSlash } from "@app/lib/fn";
|
||||
import { readLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -11,6 +12,9 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:workspaceId/secret-snapshots",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Return project secret snapshots ids",
|
||||
security: [
|
||||
@ -51,6 +55,9 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:workspaceId/secret-snapshots/count",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
@ -83,6 +90,9 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:workspaceId/audit-logs",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Return audit logs",
|
||||
security: [
|
||||
@ -145,6 +155,9 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:workspaceId/audit-logs/filters/actors",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
|
@ -17,6 +17,7 @@ import { SamlProviders, TGetSamlCfgDTO } from "@app/ee/services/saml-config/saml
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -203,8 +204,11 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/config",
|
||||
method: "GET",
|
||||
url: "/config",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
querystring: z.object({
|
||||
@ -240,8 +244,11 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/config",
|
||||
method: "POST",
|
||||
url: "/config",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
body: z.object({
|
||||
@ -270,8 +277,11 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/config",
|
||||
method: "PATCH",
|
||||
url: "/config",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
body: z
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { ScimTokensSchema } from "@app/db/schemas";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -20,6 +21,9 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/scim-tokens",
|
||||
method: "POST",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
body: z.object({
|
||||
@ -51,6 +55,9 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/scim-tokens",
|
||||
method: "GET",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
querystring: z.object({
|
||||
@ -78,6 +85,9 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/scim-tokens/:scimTokenId",
|
||||
method: "DELETE",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { nanoid } from "nanoid";
|
||||
import { z } from "zod";
|
||||
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { sapPubSchema } from "@app/server/routes/sanitizedSchemas";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
@ -9,6 +10,9 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "POST",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
body: z
|
||||
.object({
|
||||
@ -47,6 +51,9 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
server.route({
|
||||
url: "/:sapId",
|
||||
method: "PATCH",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
sapId: z.string()
|
||||
@ -85,6 +92,9 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
server.route({
|
||||
url: "/:sapId",
|
||||
method: "DELETE",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
sapId: z.string()
|
||||
@ -111,6 +121,9 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "GET",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
querystring: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
@ -137,6 +150,9 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
server.route({
|
||||
url: "/board",
|
||||
method: "GET",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
querystring: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
|
@ -10,13 +10,17 @@ import {
|
||||
} from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { ApprovalStatus, RequestState } from "@app/ee/services/secret-approval-request/secret-approval-request-types";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerSecretApprovalRequestRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "GET",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
querystring: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
@ -62,8 +66,11 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/count",
|
||||
method: "GET",
|
||||
url: "/count",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
querystring: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
@ -93,6 +100,9 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
||||
server.route({
|
||||
url: "/:id/merge",
|
||||
method: "POST",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
id: z.string()
|
||||
@ -117,8 +127,11 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:id/review",
|
||||
method: "POST",
|
||||
url: "/:id/review",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
id: z.string()
|
||||
@ -147,8 +160,11 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:id/status",
|
||||
method: "POST",
|
||||
url: "/:id/status",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
id: z.string()
|
||||
@ -203,8 +219,11 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
||||
.array()
|
||||
.optional();
|
||||
server.route({
|
||||
url: "/:id",
|
||||
method: "GET",
|
||||
url: "/:id",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
id: z.string()
|
||||
|
@ -1,12 +1,16 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { readLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerSecretRotationProviderRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/:workspaceId",
|
||||
method: "GET",
|
||||
url: "/:workspaceId",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
|
@ -2,13 +2,17 @@ import { z } from "zod";
|
||||
|
||||
import { SecretRotationOutputsSchema, SecretRotationsSchema, SecretsSchema } from "@app/db/schemas";
|
||||
import { removeTrailingSlash } from "@app/lib/fn";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerSecretRotationRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "POST",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
@ -52,6 +56,9 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) =
|
||||
server.route({
|
||||
url: "/restart",
|
||||
method: "POST",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
id: z.string().trim()
|
||||
@ -86,6 +93,9 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) =
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "GET",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
querystring: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
@ -136,8 +146,11 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) =
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:id",
|
||||
method: "DELETE",
|
||||
url: "/:id",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
id: z.string().trim()
|
||||
|
@ -2,13 +2,17 @@ import { z } from "zod";
|
||||
|
||||
import { GitAppOrgSchema, SecretScanningGitRisksSchema } from "@app/db/schemas";
|
||||
import { SecretScanningRiskStatus } from "@app/ee/services/secret-scanning/secret-scanning-types";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerSecretScanningRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/create-installation-session/organization",
|
||||
method: "POST",
|
||||
url: "/create-installation-session/organization",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({ organizationId: z.string().trim() }),
|
||||
response: {
|
||||
@ -31,8 +35,11 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/link-installation",
|
||||
method: "POST",
|
||||
url: "/link-installation",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
installationId: z.string(),
|
||||
@ -56,8 +63,11 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/installation-status/organization/:organizationId",
|
||||
method: "GET",
|
||||
url: "/installation-status/organization/:organizationId",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({ organizationId: z.string().trim() }),
|
||||
response: {
|
||||
@ -80,6 +90,9 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
|
||||
server.route({
|
||||
url: "/organization/:organizationId/risks",
|
||||
method: "GET",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({ organizationId: z.string().trim() }),
|
||||
response: {
|
||||
@ -100,8 +113,11 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/organization/:organizationId/risks/:riskId/status",
|
||||
method: "POST",
|
||||
url: "/organization/:organizationId/risks/:riskId/status",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({ organizationId: z.string().trim(), riskId: z.string().trim() }),
|
||||
body: z.object({ status: z.nativeEnum(SecretScanningRiskStatus) }),
|
||||
|
@ -1,13 +1,17 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { SecretVersionsSchema } from "@app/db/schemas";
|
||||
import { readLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerSecretVersionRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/:secretId/secret-versions",
|
||||
method: "GET",
|
||||
url: "/:secretId/secret-versions",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
secretId: z.string()
|
||||
|
@ -2,6 +2,7 @@ import { z } from "zod";
|
||||
|
||||
import { SecretSnapshotsSchema, SecretTagsSchema, SecretVersionsSchema } from "@app/db/schemas";
|
||||
import { PROJECTS } from "@app/lib/api-docs";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -9,6 +10,9 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:secretSnapshotId",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
secretSnapshotId: z.string().trim()
|
||||
@ -58,6 +62,9 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/:secretSnapshotId/rollback",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Roll back project secrets to those captured in a secret snapshot version.",
|
||||
security: [
|
||||
|
@ -2,13 +2,17 @@ import { z } from "zod";
|
||||
|
||||
import { TrustedIpsSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerTrustedIpRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/:workspaceId/trusted-ips",
|
||||
method: "GET",
|
||||
url: "/:workspaceId/trusted-ips",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
@ -33,8 +37,11 @@ export const registerTrustedIpRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:workspaceId/trusted-ips",
|
||||
method: "POST",
|
||||
url: "/:workspaceId/trusted-ips",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
@ -78,8 +85,11 @@ export const registerTrustedIpRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:workspaceId/trusted-ips/:trustedIpId",
|
||||
method: "PATCH",
|
||||
url: "/:workspaceId/trusted-ips/:trustedIpId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
@ -124,8 +134,11 @@ export const registerTrustedIpRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:workspaceId/trusted-ips/:trustedIpId",
|
||||
method: "DELETE",
|
||||
url: "/:workspaceId/trusted-ips/:trustedIpId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
|
256
backend/src/ee/routes/v1/user-additional-privilege-router.ts
Normal file
256
backend/src/ee/routes/v1/user-additional-privilege-router.ts
Normal file
@ -0,0 +1,256 @@
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
import ms from "ms";
|
||||
import { z } from "zod";
|
||||
|
||||
import { ProjectUserAdditionalPrivilegeSchema } from "@app/db/schemas";
|
||||
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-types";
|
||||
import { PROJECT_USER_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/permanent",
|
||||
method: "POST",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
projectMembershipId: z.string().min(1).describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.projectMembershipId),
|
||||
slug: z
|
||||
.string()
|
||||
.min(1)
|
||||
.max(60)
|
||||
.trim()
|
||||
.refine((v) => v.toLowerCase() === v, "Slug must be lowercase")
|
||||
.refine((v) => slugify(v) === v, {
|
||||
message: "Slug must be a valid slug"
|
||||
})
|
||||
.optional()
|
||||
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.slug),
|
||||
permissions: z.any().array().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.permissions)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
privilege: ProjectUserAdditionalPrivilegeSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const privilege = await server.services.projectUserAdditionalPrivilege.create({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
...req.body,
|
||||
slug: req.body.slug ? slugify(req.body.slug) : slugify(alphaNumericNanoId(12)),
|
||||
isTemporary: false,
|
||||
permissions: JSON.stringify(req.body.permissions)
|
||||
});
|
||||
return { privilege };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/temporary",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
projectMembershipId: z.string().min(1).describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.projectMembershipId),
|
||||
slug: z
|
||||
.string()
|
||||
.min(1)
|
||||
.max(60)
|
||||
.trim()
|
||||
.refine((v) => v.toLowerCase() === v, "Slug must be lowercase")
|
||||
.refine((v) => slugify(v) === v, {
|
||||
message: "Slug must be a valid slug"
|
||||
})
|
||||
.optional()
|
||||
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.slug),
|
||||
permissions: z.any().array().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.permissions),
|
||||
temporaryMode: z
|
||||
.nativeEnum(ProjectUserAdditionalPrivilegeTemporaryMode)
|
||||
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.temporaryMode),
|
||||
temporaryRange: z
|
||||
.string()
|
||||
.refine((val) => ms(val) > 0, "Temporary range must be a positive number")
|
||||
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.temporaryRange),
|
||||
temporaryAccessStartTime: z
|
||||
.string()
|
||||
.datetime()
|
||||
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.temporaryAccessStartTime)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
privilege: ProjectUserAdditionalPrivilegeSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const privilege = await server.services.projectUserAdditionalPrivilege.create({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
...req.body,
|
||||
slug: req.body.slug ? slugify(req.body.slug) : `privilege-${slugify(alphaNumericNanoId(12))}`,
|
||||
isTemporary: true,
|
||||
permissions: JSON.stringify(req.body.permissions)
|
||||
});
|
||||
return { privilege };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "PATCH",
|
||||
url: "/:privilegeId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
privilegeId: z.string().min(1).describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.privilegeId)
|
||||
}),
|
||||
body: z
|
||||
.object({
|
||||
slug: z
|
||||
.string()
|
||||
.max(60)
|
||||
.trim()
|
||||
.refine((v) => v.toLowerCase() === v, "Slug must be lowercase")
|
||||
.refine((v) => slugify(v) === v, {
|
||||
message: "Slug must be a valid slug"
|
||||
})
|
||||
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.slug),
|
||||
permissions: z.any().array().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.permissions),
|
||||
isTemporary: z.boolean().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.isTemporary),
|
||||
temporaryMode: z
|
||||
.nativeEnum(ProjectUserAdditionalPrivilegeTemporaryMode)
|
||||
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.temporaryMode),
|
||||
temporaryRange: z
|
||||
.string()
|
||||
.refine((val) => ms(val) > 0, "Temporary range must be a positive number")
|
||||
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.temporaryRange),
|
||||
temporaryAccessStartTime: z
|
||||
.string()
|
||||
.datetime()
|
||||
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.temporaryAccessStartTime)
|
||||
})
|
||||
.partial(),
|
||||
response: {
|
||||
200: z.object({
|
||||
privilege: ProjectUserAdditionalPrivilegeSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const privilege = await server.services.projectUserAdditionalPrivilege.updateById({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
...req.body,
|
||||
permissions: req.body.permissions ? JSON.stringify(req.body.permissions) : undefined,
|
||||
privilegeId: req.params.privilegeId
|
||||
});
|
||||
return { privilege };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/:privilegeId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
privilegeId: z.string().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.DELETE.privilegeId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
privilege: ProjectUserAdditionalPrivilegeSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const privilege = await server.services.projectUserAdditionalPrivilege.deleteById({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
privilegeId: req.params.privilegeId
|
||||
});
|
||||
return { privilege };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
querystring: z.object({
|
||||
projectMembershipId: z.string().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.LIST.projectMembershipId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
privileges: ProjectUserAdditionalPrivilegeSchema.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const privileges = await server.services.projectUserAdditionalPrivilege.listPrivileges({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
projectMembershipId: req.query.projectMembershipId
|
||||
});
|
||||
return { privileges };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:privilegeId",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
privilegeId: z.string().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.GET_BY_PRIVILEGEID.privilegeId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
privilege: ProjectUserAdditionalPrivilegeSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const privilege = await server.services.projectUserAdditionalPrivilege.getPrivilegeDetailsById({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
privilegeId: req.params.privilegeId
|
||||
});
|
||||
return { privilege };
|
||||
}
|
||||
});
|
||||
};
|
@ -8,11 +8,12 @@ import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||
import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal";
|
||||
|
||||
import { TDynamicSecretDALFactory } from "../dynamic-secret/dynamic-secret-dal";
|
||||
import { DynamicSecretProviders, TDynamicProviderFns } from "../dynamic-secret/providers/models";
|
||||
import { TProjectDALFactory } from "../project/project-dal";
|
||||
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||
import { TDynamicSecretLeaseDALFactory } from "./dynamic-secret-lease-dal";
|
||||
import { TDynamicSecretLeaseQueueServiceFactory } from "./dynamic-secret-lease-queue";
|
||||
import {
|
||||
@ -248,6 +249,7 @@ export const dynamicSecretLeaseServiceFactory = ({
|
||||
|
||||
if ((revokeResponse as { error?: Error })?.error) {
|
||||
const { error } = revokeResponse as { error?: Error };
|
||||
logger.error("Failed to revoke lease", { error: error?.message });
|
||||
const deletedDynamicSecretLease = await dynamicSecretLeaseDAL.updateById(dynamicSecretLease.id, {
|
||||
status: DynamicSecretLeaseStatus.FailedDeletion,
|
||||
statusDetails: error?.message?.slice(0, 255)
|
@ -6,11 +6,11 @@ import { TPermissionServiceFactory } from "@app/ee/services/permission/permissio
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { infisicalSymmetricDecrypt, infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||
import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal";
|
||||
|
||||
import { TDynamicSecretLeaseDALFactory } from "../dynamic-secret-lease/dynamic-secret-lease-dal";
|
||||
import { TDynamicSecretLeaseQueueServiceFactory } from "../dynamic-secret-lease/dynamic-secret-lease-queue";
|
||||
import { TProjectDALFactory } from "../project/project-dal";
|
||||
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||
import { TDynamicSecretDALFactory } from "./dynamic-secret-dal";
|
||||
import {
|
||||
DynamicSecretStatus,
|
@ -23,7 +23,17 @@ export const SqlDatabaseProvider = (): TDynamicProviderFns => {
|
||||
const dbHost = appCfg.DB_HOST || getDbConnectionHost(appCfg.DB_CONNECTION_URI);
|
||||
|
||||
const providerInputs = await DynamicSecretSqlDBSchema.parseAsync(inputs);
|
||||
if (providerInputs.host === "localhost" || providerInputs.host === "127.0.0.1" || dbHost === providerInputs.host)
|
||||
if (
|
||||
// localhost
|
||||
providerInputs.host === "localhost" ||
|
||||
providerInputs.host === "127.0.0.1" ||
|
||||
// database infisical uses
|
||||
dbHost === providerInputs.host ||
|
||||
// internal ips
|
||||
providerInputs.host === "host.docker.internal" ||
|
||||
providerInputs.host.match(/^10\.\d+\.\d+\.\d+/) ||
|
||||
providerInputs.host.match(/^192\.168\.\d+\.\d+/)
|
||||
)
|
||||
throw new BadRequestError({ message: "Invalid db host" });
|
||||
return providerInputs;
|
||||
};
|
@ -0,0 +1,12 @@
|
||||
import { TDbClient } from "@app/db";
|
||||
import { TableName } from "@app/db/schemas";
|
||||
import { ormify } from "@app/lib/knex";
|
||||
|
||||
export type TIdentityProjectAdditionalPrivilegeDALFactory = ReturnType<
|
||||
typeof identityProjectAdditionalPrivilegeDALFactory
|
||||
>;
|
||||
|
||||
export const identityProjectAdditionalPrivilegeDALFactory = (db: TDbClient) => {
|
||||
const orm = ormify(db, TableName.IdentityProjectAdditionalPrivilege);
|
||||
return orm;
|
||||
};
|
@ -0,0 +1,297 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import ms from "ms";
|
||||
|
||||
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
||||
import { BadRequestError, ForbiddenRequestError } from "@app/lib/errors";
|
||||
import { ActorType } from "@app/services/auth/auth-type";
|
||||
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||
|
||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission";
|
||||
import { TIdentityProjectAdditionalPrivilegeDALFactory } from "./identity-project-additional-privilege-dal";
|
||||
import {
|
||||
IdentityProjectAdditionalPrivilegeTemporaryMode,
|
||||
TCreateIdentityPrivilegeDTO,
|
||||
TDeleteIdentityPrivilegeDTO,
|
||||
TGetIdentityPrivilegeDetailsDTO,
|
||||
TListIdentityPrivilegesDTO,
|
||||
TUpdateIdentityPrivilegeDTO
|
||||
} from "./identity-project-additional-privilege-types";
|
||||
|
||||
type TIdentityProjectAdditionalPrivilegeServiceFactoryDep = {
|
||||
identityProjectAdditionalPrivilegeDAL: TIdentityProjectAdditionalPrivilegeDALFactory;
|
||||
identityProjectDAL: Pick<TIdentityProjectDALFactory, "findOne" | "findById">;
|
||||
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
||||
};
|
||||
|
||||
export type TIdentityProjectAdditionalPrivilegeServiceFactory = ReturnType<
|
||||
typeof identityProjectAdditionalPrivilegeServiceFactory
|
||||
>;
|
||||
|
||||
export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
||||
identityProjectAdditionalPrivilegeDAL,
|
||||
identityProjectDAL,
|
||||
permissionService,
|
||||
projectDAL
|
||||
}: TIdentityProjectAdditionalPrivilegeServiceFactoryDep) => {
|
||||
const create = async ({
|
||||
slug,
|
||||
actor,
|
||||
actorId,
|
||||
identityId,
|
||||
projectSlug,
|
||||
permissions: customPermission,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
...dto
|
||||
}: TCreateIdentityPrivilegeDTO) => {
|
||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
||||
if (!project) throw new BadRequestError({ message: "Project not found" });
|
||||
const projectId = project.id;
|
||||
|
||||
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
|
||||
if (!identityProjectMembership)
|
||||
throw new BadRequestError({ message: `Failed to find identity with id ${identityId}` });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
identityProjectMembership.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
|
||||
const { permission: identityRolePermission } = await permissionService.getProjectPermission(
|
||||
ActorType.IDENTITY,
|
||||
identityId,
|
||||
identityProjectMembership.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
|
||||
if (!hasRequiredPriviledges)
|
||||
throw new ForbiddenRequestError({ message: "Failed to update more privileged identity" });
|
||||
|
||||
const existingSlug = await identityProjectAdditionalPrivilegeDAL.findOne({
|
||||
slug,
|
||||
projectMembershipId: identityProjectMembership.id
|
||||
});
|
||||
if (existingSlug) throw new BadRequestError({ message: "Additional privilege of provided slug exist" });
|
||||
|
||||
if (!dto.isTemporary) {
|
||||
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.create({
|
||||
projectMembershipId: identityProjectMembership.id,
|
||||
slug,
|
||||
permissions: customPermission
|
||||
});
|
||||
return additionalPrivilege;
|
||||
}
|
||||
|
||||
const relativeTempAllocatedTimeInMs = ms(dto.temporaryRange);
|
||||
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.create({
|
||||
projectMembershipId: identityProjectMembership.id,
|
||||
slug,
|
||||
permissions: customPermission,
|
||||
isTemporary: true,
|
||||
temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative,
|
||||
temporaryRange: dto.temporaryRange,
|
||||
temporaryAccessStartTime: new Date(dto.temporaryAccessStartTime),
|
||||
temporaryAccessEndTime: new Date(new Date(dto.temporaryAccessStartTime).getTime() + relativeTempAllocatedTimeInMs)
|
||||
});
|
||||
return additionalPrivilege;
|
||||
};
|
||||
|
||||
const updateBySlug = async ({
|
||||
projectSlug,
|
||||
slug,
|
||||
identityId,
|
||||
data,
|
||||
actorOrgId,
|
||||
actor,
|
||||
actorId,
|
||||
actorAuthMethod
|
||||
}: TUpdateIdentityPrivilegeDTO) => {
|
||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
||||
if (!project) throw new BadRequestError({ message: "Project not found" });
|
||||
const projectId = project.id;
|
||||
|
||||
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
|
||||
if (!identityProjectMembership)
|
||||
throw new BadRequestError({ message: `Failed to find identity with id ${identityId}` });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
identityProjectMembership.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
|
||||
const { permission: identityRolePermission } = await permissionService.getProjectPermission(
|
||||
ActorType.IDENTITY,
|
||||
identityProjectMembership.identityId,
|
||||
identityProjectMembership.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
|
||||
if (!hasRequiredPriviledges)
|
||||
throw new ForbiddenRequestError({ message: "Failed to update more privileged identity" });
|
||||
|
||||
const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findOne({
|
||||
slug,
|
||||
projectMembershipId: identityProjectMembership.id
|
||||
});
|
||||
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
|
||||
if (data?.slug) {
|
||||
const existingSlug = await identityProjectAdditionalPrivilegeDAL.findOne({
|
||||
slug: data.slug,
|
||||
projectMembershipId: identityProjectMembership.id
|
||||
});
|
||||
if (existingSlug && existingSlug.id !== identityPrivilege.id)
|
||||
throw new BadRequestError({ message: "Additional privilege of provided slug exist" });
|
||||
}
|
||||
|
||||
const isTemporary = typeof data?.isTemporary !== "undefined" ? data.isTemporary : identityPrivilege.isTemporary;
|
||||
if (isTemporary) {
|
||||
const temporaryAccessStartTime = data?.temporaryAccessStartTime || identityPrivilege?.temporaryAccessStartTime;
|
||||
const temporaryRange = data?.temporaryRange || identityPrivilege?.temporaryRange;
|
||||
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, {
|
||||
...data,
|
||||
temporaryAccessStartTime: new Date(temporaryAccessStartTime || ""),
|
||||
temporaryAccessEndTime: new Date(new Date(temporaryAccessStartTime || "").getTime() + ms(temporaryRange || ""))
|
||||
});
|
||||
return additionalPrivilege;
|
||||
}
|
||||
|
||||
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, {
|
||||
...data,
|
||||
isTemporary: false,
|
||||
temporaryAccessStartTime: null,
|
||||
temporaryAccessEndTime: null,
|
||||
temporaryRange: null,
|
||||
temporaryMode: null
|
||||
});
|
||||
return additionalPrivilege;
|
||||
};
|
||||
|
||||
const deleteBySlug = async ({
|
||||
actorId,
|
||||
slug,
|
||||
identityId,
|
||||
projectSlug,
|
||||
actor,
|
||||
actorOrgId,
|
||||
actorAuthMethod
|
||||
}: TDeleteIdentityPrivilegeDTO) => {
|
||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
||||
if (!project) throw new BadRequestError({ message: "Project not found" });
|
||||
const projectId = project.id;
|
||||
|
||||
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
|
||||
if (!identityProjectMembership)
|
||||
throw new BadRequestError({ message: `Failed to find identity with id ${identityId}` });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
identityProjectMembership.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
|
||||
const { permission: identityRolePermission } = await permissionService.getProjectPermission(
|
||||
ActorType.IDENTITY,
|
||||
identityProjectMembership.identityId,
|
||||
identityProjectMembership.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
|
||||
if (!hasRequiredPriviledges)
|
||||
throw new ForbiddenRequestError({ message: "Failed to edit more privileged identity" });
|
||||
|
||||
const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findOne({
|
||||
slug,
|
||||
projectMembershipId: identityProjectMembership.id
|
||||
});
|
||||
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
|
||||
|
||||
const deletedPrivilege = await identityProjectAdditionalPrivilegeDAL.deleteById(identityPrivilege.id);
|
||||
return deletedPrivilege;
|
||||
};
|
||||
|
||||
const getPrivilegeDetailsBySlug = async ({
|
||||
projectSlug,
|
||||
identityId,
|
||||
slug,
|
||||
actorOrgId,
|
||||
actor,
|
||||
actorId,
|
||||
actorAuthMethod
|
||||
}: TGetIdentityPrivilegeDetailsDTO) => {
|
||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
||||
if (!project) throw new BadRequestError({ message: "Project not found" });
|
||||
const projectId = project.id;
|
||||
|
||||
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
|
||||
if (!identityProjectMembership)
|
||||
throw new BadRequestError({ message: `Failed to find identity with id ${identityId}` });
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
identityProjectMembership.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
|
||||
|
||||
const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findOne({
|
||||
slug,
|
||||
projectMembershipId: identityProjectMembership.id
|
||||
});
|
||||
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
|
||||
|
||||
return identityPrivilege;
|
||||
};
|
||||
|
||||
const listIdentityProjectPrivileges = async ({
|
||||
identityId,
|
||||
actorOrgId,
|
||||
actor,
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
projectSlug
|
||||
}: TListIdentityPrivilegesDTO) => {
|
||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
||||
if (!project) throw new BadRequestError({ message: "Project not found" });
|
||||
const projectId = project.id;
|
||||
|
||||
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
|
||||
if (!identityProjectMembership)
|
||||
throw new BadRequestError({ message: `Failed to find identity with id ${identityId}` });
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
identityProjectMembership.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
|
||||
|
||||
const identityPrivileges = await identityProjectAdditionalPrivilegeDAL.find({
|
||||
projectMembershipId: identityProjectMembership.id
|
||||
});
|
||||
return identityPrivileges;
|
||||
};
|
||||
|
||||
return {
|
||||
create,
|
||||
updateBySlug,
|
||||
deleteBySlug,
|
||||
getPrivilegeDetailsBySlug,
|
||||
listIdentityProjectPrivileges
|
||||
};
|
||||
};
|
@ -0,0 +1,54 @@
|
||||
import { TProjectPermission } from "@app/lib/types";
|
||||
|
||||
export enum IdentityProjectAdditionalPrivilegeTemporaryMode {
|
||||
Relative = "relative"
|
||||
}
|
||||
|
||||
export type TCreateIdentityPrivilegeDTO = {
|
||||
permissions: unknown;
|
||||
identityId: string;
|
||||
projectSlug: string;
|
||||
slug: string;
|
||||
} & (
|
||||
| {
|
||||
isTemporary: false;
|
||||
}
|
||||
| {
|
||||
isTemporary: true;
|
||||
temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative;
|
||||
temporaryRange: string;
|
||||
temporaryAccessStartTime: string;
|
||||
}
|
||||
) &
|
||||
Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TUpdateIdentityPrivilegeDTO = { slug: string; identityId: string; projectSlug: string } & Omit<
|
||||
TProjectPermission,
|
||||
"projectId"
|
||||
> & {
|
||||
data: Partial<{
|
||||
permissions: unknown;
|
||||
slug: string;
|
||||
isTemporary: boolean;
|
||||
temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative;
|
||||
temporaryRange: string;
|
||||
temporaryAccessStartTime: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
export type TDeleteIdentityPrivilegeDTO = Omit<TProjectPermission, "projectId"> & {
|
||||
slug: string;
|
||||
identityId: string;
|
||||
projectSlug: string;
|
||||
};
|
||||
|
||||
export type TGetIdentityPrivilegeDetailsDTO = Omit<TProjectPermission, "projectId"> & {
|
||||
slug: string;
|
||||
identityId: string;
|
||||
projectSlug: string;
|
||||
};
|
||||
|
||||
export type TListIdentityPrivilegesDTO = Omit<TProjectPermission, "projectId"> & {
|
||||
identityId: string;
|
||||
projectSlug: string;
|
||||
};
|
@ -15,7 +15,7 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
|
||||
membersUsed: 0,
|
||||
environmentLimit: null,
|
||||
environmentsUsed: 0,
|
||||
dynamicSecret: true,
|
||||
dynamicSecret: false,
|
||||
secretVersioning: true,
|
||||
pitRecovery: false,
|
||||
ipAllowlisting: false,
|
||||
|
@ -27,7 +27,7 @@ export type TFeatureSet = {
|
||||
tier: -1;
|
||||
workspaceLimit: null;
|
||||
workspacesUsed: 0;
|
||||
dynamicSecret: true;
|
||||
dynamicSecret: false;
|
||||
memberLimit: null;
|
||||
membersUsed: 0;
|
||||
environmentLimit: null;
|
||||
|
@ -56,6 +56,11 @@ export const permissionDALFactory = (db: TDbClient) => {
|
||||
`${TableName.ProjectUserMembershipRole}.customRoleId`,
|
||||
`${TableName.ProjectRoles}.id`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.ProjectUserAdditionalPrivilege,
|
||||
`${TableName.ProjectUserAdditionalPrivilege}.projectMembershipId`,
|
||||
`${TableName.ProjectMembership}.id`
|
||||
)
|
||||
.join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`)
|
||||
.join(TableName.Organization, `${TableName.Project}.orgId`, `${TableName.Organization}.id`)
|
||||
.where("userId", userId)
|
||||
@ -69,9 +74,22 @@ export const permissionDALFactory = (db: TDbClient) => {
|
||||
db.ref("updatedAt").withSchema(TableName.ProjectMembership).as("membershipUpdatedAt"),
|
||||
db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"),
|
||||
db.ref("orgId").withSchema(TableName.Project),
|
||||
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug")
|
||||
)
|
||||
.select("permissions");
|
||||
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"),
|
||||
db.ref("permissions").withSchema(TableName.ProjectRoles),
|
||||
db.ref("id").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApId"),
|
||||
db.ref("permissions").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApPermissions"),
|
||||
db.ref("temporaryMode").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApTemporaryMode"),
|
||||
db.ref("isTemporary").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApIsTemporary"),
|
||||
db.ref("temporaryRange").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApTemporaryRange"),
|
||||
db
|
||||
.ref("temporaryAccessStartTime")
|
||||
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
||||
.as("userApTemporaryAccessStartTime"),
|
||||
db
|
||||
.ref("temporaryAccessEndTime")
|
||||
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
||||
.as("userApTemporaryAccessEndTime")
|
||||
);
|
||||
|
||||
const permission = sqlNestRelationships({
|
||||
data: docs,
|
||||
@ -102,15 +120,44 @@ export const permissionDALFactory = (db: TDbClient) => {
|
||||
permissions: z.unknown(),
|
||||
customRoleSlug: z.string().optional().nullable()
|
||||
}).parse(data)
|
||||
},
|
||||
{
|
||||
key: "userApId",
|
||||
label: "additionalPrivileges" as const,
|
||||
mapper: ({
|
||||
userApId,
|
||||
userApPermissions,
|
||||
userApIsTemporary,
|
||||
userApTemporaryMode,
|
||||
userApTemporaryRange,
|
||||
userApTemporaryAccessEndTime,
|
||||
userApTemporaryAccessStartTime
|
||||
}) => ({
|
||||
id: userApId,
|
||||
permissions: userApPermissions,
|
||||
temporaryRange: userApTemporaryRange,
|
||||
temporaryMode: userApTemporaryMode,
|
||||
temporaryAccessEndTime: userApTemporaryAccessEndTime,
|
||||
temporaryAccessStartTime: userApTemporaryAccessStartTime,
|
||||
isTemporary: userApIsTemporary
|
||||
})
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (!permission?.[0]) return undefined;
|
||||
// when introducting cron mode change it here
|
||||
const activeRoles = permission?.[0]?.roles.filter(
|
||||
const activeRoles = permission?.[0]?.roles?.filter(
|
||||
({ isTemporary, temporaryAccessEndTime }) =>
|
||||
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
|
||||
);
|
||||
return permission?.[0] ? { ...permission[0], roles: activeRoles } : undefined;
|
||||
|
||||
const activeAdditionalPrivileges = permission?.[0]?.additionalPrivileges?.filter(
|
||||
({ isTemporary, temporaryAccessEndTime }) =>
|
||||
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
|
||||
);
|
||||
|
||||
return { ...permission[0], roles: activeRoles, additionalPrivileges: activeAdditionalPrivileges };
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "GetProjectPermission" });
|
||||
}
|
||||
@ -129,6 +176,11 @@ export const permissionDALFactory = (db: TDbClient) => {
|
||||
`${TableName.IdentityProjectMembershipRole}.customRoleId`,
|
||||
`${TableName.ProjectRoles}.id`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityProjectAdditionalPrivilege,
|
||||
`${TableName.IdentityProjectAdditionalPrivilege}.projectMembershipId`,
|
||||
`${TableName.IdentityProjectMembership}.id`
|
||||
)
|
||||
.join(
|
||||
// Join the Project table to later select orgId
|
||||
TableName.Project,
|
||||
@ -144,9 +196,28 @@ export const permissionDALFactory = (db: TDbClient) => {
|
||||
db.ref("role").withSchema(TableName.IdentityProjectMembership).as("oldRoleField"),
|
||||
db.ref("createdAt").withSchema(TableName.IdentityProjectMembership).as("membershipCreatedAt"),
|
||||
db.ref("updatedAt").withSchema(TableName.IdentityProjectMembership).as("membershipUpdatedAt"),
|
||||
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug")
|
||||
)
|
||||
.select("permissions");
|
||||
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"),
|
||||
db.ref("permissions").withSchema(TableName.ProjectRoles),
|
||||
db.ref("id").withSchema(TableName.IdentityProjectAdditionalPrivilege).as("identityApId"),
|
||||
db.ref("permissions").withSchema(TableName.IdentityProjectAdditionalPrivilege).as("identityApPermissions"),
|
||||
db
|
||||
.ref("temporaryMode")
|
||||
.withSchema(TableName.IdentityProjectAdditionalPrivilege)
|
||||
.as("identityApTemporaryMode"),
|
||||
db.ref("isTemporary").withSchema(TableName.IdentityProjectAdditionalPrivilege).as("identityApIsTemporary"),
|
||||
db
|
||||
.ref("temporaryRange")
|
||||
.withSchema(TableName.IdentityProjectAdditionalPrivilege)
|
||||
.as("identityApTemporaryRange"),
|
||||
db
|
||||
.ref("temporaryAccessStartTime")
|
||||
.withSchema(TableName.IdentityProjectAdditionalPrivilege)
|
||||
.as("identityApTemporaryAccessStartTime"),
|
||||
db
|
||||
.ref("temporaryAccessEndTime")
|
||||
.withSchema(TableName.IdentityProjectAdditionalPrivilege)
|
||||
.as("identityApTemporaryAccessEndTime")
|
||||
);
|
||||
|
||||
const permission = sqlNestRelationships({
|
||||
data: docs,
|
||||
@ -171,16 +242,44 @@ export const permissionDALFactory = (db: TDbClient) => {
|
||||
permissions: z.unknown(),
|
||||
customRoleSlug: z.string().optional().nullable()
|
||||
}).parse(data)
|
||||
},
|
||||
{
|
||||
key: "identityApId",
|
||||
label: "additionalPrivileges" as const,
|
||||
mapper: ({
|
||||
identityApId,
|
||||
identityApPermissions,
|
||||
identityApIsTemporary,
|
||||
identityApTemporaryMode,
|
||||
identityApTemporaryRange,
|
||||
identityApTemporaryAccessEndTime,
|
||||
identityApTemporaryAccessStartTime
|
||||
}) => ({
|
||||
id: identityApId,
|
||||
permissions: identityApPermissions,
|
||||
temporaryRange: identityApTemporaryRange,
|
||||
temporaryMode: identityApTemporaryMode,
|
||||
temporaryAccessEndTime: identityApTemporaryAccessEndTime,
|
||||
temporaryAccessStartTime: identityApTemporaryAccessStartTime,
|
||||
isTemporary: identityApIsTemporary
|
||||
})
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (!permission?.[0]) return undefined;
|
||||
|
||||
// when introducting cron mode change it here
|
||||
const activeRoles = permission?.[0]?.roles.filter(
|
||||
({ isTemporary, temporaryAccessEndTime }) =>
|
||||
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
|
||||
);
|
||||
return permission?.[0] ? { ...permission[0], roles: activeRoles } : undefined;
|
||||
const activeAdditionalPrivileges = permission?.[0]?.additionalPrivileges?.filter(
|
||||
({ isTemporary, temporaryAccessEndTime }) =>
|
||||
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
|
||||
);
|
||||
|
||||
return { ...permission[0], roles: activeRoles, additionalPrivileges: activeAdditionalPrivileges };
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "GetProjectIdentityPermission" });
|
||||
}
|
||||
|
@ -5,9 +5,13 @@ import { ActorAuthMethod, AuthMethod } from "@app/services/auth/auth-type";
|
||||
function isAuthMethodSaml(actorAuthMethod: ActorAuthMethod) {
|
||||
if (!actorAuthMethod) return false;
|
||||
|
||||
return [AuthMethod.AZURE_SAML, AuthMethod.OKTA_SAML, AuthMethod.JUMPCLOUD_SAML, AuthMethod.GOOGLE_SAML].includes(
|
||||
actorAuthMethod
|
||||
);
|
||||
return [
|
||||
AuthMethod.AZURE_SAML,
|
||||
AuthMethod.OKTA_SAML,
|
||||
AuthMethod.JUMPCLOUD_SAML,
|
||||
AuthMethod.GOOGLE_SAML,
|
||||
AuthMethod.KEYCLOAK_SAML
|
||||
].includes(actorAuthMethod);
|
||||
}
|
||||
|
||||
function validateOrgSAML(actorAuthMethod: ActorAuthMethod, isSamlEnforced: TOrganizations["authEnforced"]) {
|
||||
|
@ -180,10 +180,12 @@ export const permissionServiceFactory = ({
|
||||
authMethod: ActorAuthMethod,
|
||||
userOrgId?: string
|
||||
): Promise<TProjectPermissionRT<ActorType.USER>> => {
|
||||
const membership = await permissionDAL.getProjectPermission(userId, projectId);
|
||||
if (!membership) throw new UnauthorizedError({ name: "User not in project" });
|
||||
const userProjectPermission = await permissionDAL.getProjectPermission(userId, projectId);
|
||||
if (!userProjectPermission) throw new UnauthorizedError({ name: "User not in project" });
|
||||
|
||||
if (membership.roles.some(({ role, permissions }) => role === ProjectMembershipRole.Custom && !permissions)) {
|
||||
if (
|
||||
userProjectPermission.roles.some(({ role, permissions }) => role === ProjectMembershipRole.Custom && !permissions)
|
||||
) {
|
||||
throw new BadRequestError({ name: "Custom permission not found" });
|
||||
}
|
||||
|
||||
@ -192,17 +194,27 @@ export const permissionServiceFactory = ({
|
||||
|
||||
// Extra: This means that when users are using API keys to make requests, they can't use slug-based routes.
|
||||
// Slug-based routes depend on the organization ID being present on the request, since project slugs aren't globally unique, and we need a way to filter by organization.
|
||||
if (userOrgId !== "API_KEY" && membership.orgId !== userOrgId) {
|
||||
if (userOrgId !== "API_KEY" && userProjectPermission.orgId !== userOrgId) {
|
||||
throw new UnauthorizedError({ name: "You are not logged into this organization" });
|
||||
}
|
||||
|
||||
validateOrgSAML(authMethod, membership.orgAuthEnforced);
|
||||
validateOrgSAML(authMethod, userProjectPermission.orgAuthEnforced);
|
||||
|
||||
// join two permissions and pass to build the final permission set
|
||||
const rolePermissions = userProjectPermission.roles?.map(({ role, permissions }) => ({ role, permissions })) || [];
|
||||
const additionalPrivileges =
|
||||
userProjectPermission.additionalPrivileges?.map(({ permissions }) => ({
|
||||
role: ProjectMembershipRole.Custom,
|
||||
permissions
|
||||
})) || [];
|
||||
|
||||
return {
|
||||
permission: buildProjectPermission(membership.roles),
|
||||
membership,
|
||||
permission: buildProjectPermission(rolePermissions.concat(additionalPrivileges)),
|
||||
membership: userProjectPermission,
|
||||
hasRole: (role: string) =>
|
||||
membership.roles.findIndex(({ role: slug, customRoleSlug }) => role === slug || slug === customRoleSlug) !== -1
|
||||
userProjectPermission.roles.findIndex(
|
||||
({ role: slug, customRoleSlug }) => role === slug || slug === customRoleSlug
|
||||
) !== -1
|
||||
};
|
||||
};
|
||||
|
||||
@ -226,8 +238,16 @@ export const permissionServiceFactory = ({
|
||||
throw new UnauthorizedError({ name: "You are not a member of this organization" });
|
||||
}
|
||||
|
||||
const rolePermissions =
|
||||
identityProjectPermission.roles?.map(({ role, permissions }) => ({ role, permissions })) || [];
|
||||
const additionalPrivileges =
|
||||
identityProjectPermission.additionalPrivileges?.map(({ permissions }) => ({
|
||||
role: ProjectMembershipRole.Custom,
|
||||
permissions
|
||||
})) || [];
|
||||
|
||||
return {
|
||||
permission: buildProjectPermission(identityProjectPermission.roles),
|
||||
permission: buildProjectPermission(rolePermissions.concat(additionalPrivileges)),
|
||||
membership: identityProjectPermission,
|
||||
hasRole: (role: string) =>
|
||||
identityProjectPermission.roles.findIndex(
|
||||
|
@ -0,0 +1,10 @@
|
||||
import { TDbClient } from "@app/db";
|
||||
import { TableName } from "@app/db/schemas";
|
||||
import { ormify } from "@app/lib/knex";
|
||||
|
||||
export type TProjectUserAdditionalPrivilegeDALFactory = ReturnType<typeof projectUserAdditionalPrivilegeDALFactory>;
|
||||
|
||||
export const projectUserAdditionalPrivilegeDALFactory = (db: TDbClient) => {
|
||||
const orm = ormify(db, TableName.ProjectUserAdditionalPrivilege);
|
||||
return orm;
|
||||
};
|
@ -0,0 +1,212 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import ms from "ms";
|
||||
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
||||
|
||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission";
|
||||
import { TProjectUserAdditionalPrivilegeDALFactory } from "./project-user-additional-privilege-dal";
|
||||
import {
|
||||
ProjectUserAdditionalPrivilegeTemporaryMode,
|
||||
TCreateUserPrivilegeDTO,
|
||||
TDeleteUserPrivilegeDTO,
|
||||
TGetUserPrivilegeDetailsDTO,
|
||||
TListUserPrivilegesDTO,
|
||||
TUpdateUserPrivilegeDTO
|
||||
} from "./project-user-additional-privilege-types";
|
||||
|
||||
type TProjectUserAdditionalPrivilegeServiceFactoryDep = {
|
||||
projectUserAdditionalPrivilegeDAL: TProjectUserAdditionalPrivilegeDALFactory;
|
||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "findById">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
||||
};
|
||||
|
||||
export type TProjectUserAdditionalPrivilegeServiceFactory = ReturnType<
|
||||
typeof projectUserAdditionalPrivilegeServiceFactory
|
||||
>;
|
||||
|
||||
export const projectUserAdditionalPrivilegeServiceFactory = ({
|
||||
projectUserAdditionalPrivilegeDAL,
|
||||
projectMembershipDAL,
|
||||
permissionService
|
||||
}: TProjectUserAdditionalPrivilegeServiceFactoryDep) => {
|
||||
const create = async ({
|
||||
slug,
|
||||
actor,
|
||||
actorId,
|
||||
permissions: customPermission,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
projectMembershipId,
|
||||
...dto
|
||||
}: TCreateUserPrivilegeDTO) => {
|
||||
const projectMembership = await projectMembershipDAL.findById(projectMembershipId);
|
||||
if (!projectMembership) throw new BadRequestError({ message: "Project membership not found" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectMembership.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member);
|
||||
|
||||
const existingSlug = await projectUserAdditionalPrivilegeDAL.findOne({ slug, projectMembershipId });
|
||||
if (existingSlug) throw new BadRequestError({ message: "Additional privilege of provided slug exist" });
|
||||
|
||||
if (!dto.isTemporary) {
|
||||
const additionalPrivilege = await projectUserAdditionalPrivilegeDAL.create({
|
||||
projectMembershipId,
|
||||
slug,
|
||||
permissions: customPermission
|
||||
});
|
||||
return additionalPrivilege;
|
||||
}
|
||||
|
||||
const relativeTempAllocatedTimeInMs = ms(dto.temporaryRange);
|
||||
const additionalPrivilege = await projectUserAdditionalPrivilegeDAL.create({
|
||||
projectMembershipId,
|
||||
slug,
|
||||
permissions: customPermission,
|
||||
isTemporary: true,
|
||||
temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative,
|
||||
temporaryRange: dto.temporaryRange,
|
||||
temporaryAccessStartTime: new Date(dto.temporaryAccessStartTime),
|
||||
temporaryAccessEndTime: new Date(new Date(dto.temporaryAccessStartTime).getTime() + relativeTempAllocatedTimeInMs)
|
||||
});
|
||||
return additionalPrivilege;
|
||||
};
|
||||
|
||||
const updateById = async ({
|
||||
privilegeId,
|
||||
actorOrgId,
|
||||
actor,
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
...dto
|
||||
}: TUpdateUserPrivilegeDTO) => {
|
||||
const userPrivilege = await projectUserAdditionalPrivilegeDAL.findById(privilegeId);
|
||||
if (!userPrivilege) throw new BadRequestError({ message: "User additional privilege not found" });
|
||||
|
||||
const projectMembership = await projectMembershipDAL.findById(userPrivilege.projectMembershipId);
|
||||
if (!projectMembership) throw new BadRequestError({ message: "Project membership not found" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectMembership.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member);
|
||||
|
||||
if (dto?.slug) {
|
||||
const existingSlug = await projectUserAdditionalPrivilegeDAL.findOne({
|
||||
slug: dto.slug,
|
||||
projectMembershipId: projectMembership.id
|
||||
});
|
||||
if (existingSlug && existingSlug.id !== userPrivilege.id)
|
||||
throw new BadRequestError({ message: "Additional privilege of provided slug exist" });
|
||||
}
|
||||
|
||||
const isTemporary = typeof dto?.isTemporary !== "undefined" ? dto.isTemporary : userPrivilege.isTemporary;
|
||||
if (isTemporary) {
|
||||
const temporaryAccessStartTime = dto?.temporaryAccessStartTime || userPrivilege?.temporaryAccessStartTime;
|
||||
const temporaryRange = dto?.temporaryRange || userPrivilege?.temporaryRange;
|
||||
const additionalPrivilege = await projectUserAdditionalPrivilegeDAL.updateById(userPrivilege.id, {
|
||||
...dto,
|
||||
temporaryAccessStartTime: new Date(temporaryAccessStartTime || ""),
|
||||
temporaryAccessEndTime: new Date(new Date(temporaryAccessStartTime || "").getTime() + ms(temporaryRange || ""))
|
||||
});
|
||||
return additionalPrivilege;
|
||||
}
|
||||
|
||||
const additionalPrivilege = await projectUserAdditionalPrivilegeDAL.updateById(userPrivilege.id, {
|
||||
...dto,
|
||||
isTemporary: false,
|
||||
temporaryAccessStartTime: null,
|
||||
temporaryAccessEndTime: null,
|
||||
temporaryRange: null,
|
||||
temporaryMode: null
|
||||
});
|
||||
return additionalPrivilege;
|
||||
};
|
||||
|
||||
const deleteById = async ({ actorId, actor, actorOrgId, actorAuthMethod, privilegeId }: TDeleteUserPrivilegeDTO) => {
|
||||
const userPrivilege = await projectUserAdditionalPrivilegeDAL.findById(privilegeId);
|
||||
if (!userPrivilege) throw new BadRequestError({ message: "User additional privilege not found" });
|
||||
|
||||
const projectMembership = await projectMembershipDAL.findById(userPrivilege.projectMembershipId);
|
||||
if (!projectMembership) throw new BadRequestError({ message: "Project membership not found" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectMembership.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member);
|
||||
|
||||
const deletedPrivilege = await projectUserAdditionalPrivilegeDAL.deleteById(userPrivilege.id);
|
||||
return deletedPrivilege;
|
||||
};
|
||||
|
||||
const getPrivilegeDetailsById = async ({
|
||||
privilegeId,
|
||||
actorOrgId,
|
||||
actor,
|
||||
actorId,
|
||||
actorAuthMethod
|
||||
}: TGetUserPrivilegeDetailsDTO) => {
|
||||
const userPrivilege = await projectUserAdditionalPrivilegeDAL.findById(privilegeId);
|
||||
if (!userPrivilege) throw new BadRequestError({ message: "User additional privilege not found" });
|
||||
|
||||
const projectMembership = await projectMembershipDAL.findById(userPrivilege.projectMembershipId);
|
||||
if (!projectMembership) throw new BadRequestError({ message: "Project membership not found" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectMembership.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Member);
|
||||
|
||||
return userPrivilege;
|
||||
};
|
||||
|
||||
const listPrivileges = async ({
|
||||
projectMembershipId,
|
||||
actorOrgId,
|
||||
actor,
|
||||
actorId,
|
||||
actorAuthMethod
|
||||
}: TListUserPrivilegesDTO) => {
|
||||
const projectMembership = await projectMembershipDAL.findById(projectMembershipId);
|
||||
if (!projectMembership) throw new BadRequestError({ message: "Project membership not found" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectMembership.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Member);
|
||||
|
||||
const userPrivileges = await projectUserAdditionalPrivilegeDAL.find({ projectMembershipId });
|
||||
return userPrivileges;
|
||||
};
|
||||
|
||||
return {
|
||||
create,
|
||||
updateById,
|
||||
deleteById,
|
||||
getPrivilegeDetailsById,
|
||||
listPrivileges
|
||||
};
|
||||
};
|
@ -0,0 +1,40 @@
|
||||
import { TProjectPermission } from "@app/lib/types";
|
||||
|
||||
export enum ProjectUserAdditionalPrivilegeTemporaryMode {
|
||||
Relative = "relative"
|
||||
}
|
||||
|
||||
export type TCreateUserPrivilegeDTO = (
|
||||
| {
|
||||
permissions: unknown;
|
||||
projectMembershipId: string;
|
||||
slug: string;
|
||||
isTemporary: false;
|
||||
}
|
||||
| {
|
||||
permissions: unknown;
|
||||
projectMembershipId: string;
|
||||
slug: string;
|
||||
isTemporary: true;
|
||||
temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative;
|
||||
temporaryRange: string;
|
||||
temporaryAccessStartTime: string;
|
||||
}
|
||||
) &
|
||||
Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TUpdateUserPrivilegeDTO = { privilegeId: string } & Omit<TProjectPermission, "projectId"> &
|
||||
Partial<{
|
||||
permissions: unknown;
|
||||
slug: string;
|
||||
isTemporary: boolean;
|
||||
temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative;
|
||||
temporaryRange: string;
|
||||
temporaryAccessStartTime: string;
|
||||
}>;
|
||||
|
||||
export type TDeleteUserPrivilegeDTO = Omit<TProjectPermission, "projectId"> & { privilegeId: string };
|
||||
|
||||
export type TGetUserPrivilegeDetailsDTO = Omit<TProjectPermission, "projectId"> & { privilegeId: string };
|
||||
|
||||
export type TListUserPrivilegesDTO = Omit<TProjectPermission, "projectId"> & { projectMembershipId: string };
|
@ -319,6 +319,11 @@ export const samlConfigServiceFactory = ({
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
if (!organization) throw new BadRequestError({ message: "Org not found" });
|
||||
|
||||
// TODO(dangtony98): remove this after aliases update
|
||||
if (authProvider === AuthMethod.KEYCLOAK_SAML && appCfg.LICENSE_SERVER_KEY) {
|
||||
throw new BadRequestError({ message: "Keycloak SAML is not yet available on Infisical Cloud" });
|
||||
}
|
||||
|
||||
if (user) {
|
||||
await userDAL.transaction(async (tx) => {
|
||||
const [orgMembership] = await orgDAL.findMembership(
|
||||
|
@ -5,7 +5,8 @@ export enum SamlProviders {
|
||||
OKTA_SAML = "okta-saml",
|
||||
AZURE_SAML = "azure-saml",
|
||||
JUMPCLOUD_SAML = "jumpcloud-saml",
|
||||
GOOGLE_SAML = "google-saml"
|
||||
GOOGLE_SAML = "google-saml",
|
||||
KEYCLOAK_SAML = "keycloak-saml"
|
||||
}
|
||||
|
||||
export type TCreateSamlCfgDTO = {
|
||||
|
@ -90,7 +90,17 @@ export const secretRotationDbFn = async ({
|
||||
const appCfg = getConfig();
|
||||
|
||||
const ssl = ca ? { rejectUnauthorized: false, ca } : undefined;
|
||||
if (host === "localhost" || host === "127.0.0.1" || getDbConnectionHost(appCfg.DB_CONNECTION_URI) === host)
|
||||
const dbHost = appCfg.DB_HOST || getDbConnectionHost(appCfg.DB_CONNECTION_URI);
|
||||
if (
|
||||
host === "localhost" ||
|
||||
host === "127.0.0.1" ||
|
||||
// database infisical uses
|
||||
dbHost === host ||
|
||||
// internal ips
|
||||
host === "host.docker.internal" ||
|
||||
host.match(/^10\.\d+\.\d+\.\d+/) ||
|
||||
host.match(/^192\.168\.\d+\.\d+/)
|
||||
)
|
||||
throw new Error("Invalid db host");
|
||||
|
||||
const db = knex({
|
||||
|
@ -215,6 +215,7 @@ export const SECRETS = {
|
||||
|
||||
export const RAW_SECRETS = {
|
||||
LIST: {
|
||||
recursive: "Whether or not to fetch all secrets from the specified base path, and all of its subdirectories.",
|
||||
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.",
|
||||
environment: "The slug of the environment to list secrets from.",
|
||||
@ -397,3 +398,158 @@ export const SECRET_TAGS = {
|
||||
projectId: "The ID of the project to delete the tag from."
|
||||
}
|
||||
} as const;
|
||||
|
||||
export const IDENTITY_ADDITIONAL_PRIVILEGE = {
|
||||
CREATE: {
|
||||
projectSlug: "The slug of the project of the identity in.",
|
||||
identityId: "The ID of the identity to delete.",
|
||||
slug: "The slug of the privilege to create.",
|
||||
permissions: `The permission object for the privilege.
|
||||
1. [["read", "secrets", {environment: "dev", secretPath: {$glob: "/"}}]]
|
||||
2. [["read", "secrets", {environment: "dev"}], ["create", "secrets", {environment: "dev"}]]
|
||||
2. [["read", "secrets", {environment: "dev"}]]
|
||||
`,
|
||||
isPackPermission: "Whether the server should pack(compact) the permission object.",
|
||||
isTemporary: "Whether the privilege is temporary.",
|
||||
temporaryMode: "Type of temporary access given. Types: relative",
|
||||
temporaryRange: "TTL for the temporay time. Eg: 1m, 1h, 1d",
|
||||
temporaryAccessStartTime: "ISO time for which temporary access should begin."
|
||||
},
|
||||
UPDATE: {
|
||||
projectSlug: "The slug of the project of the identity in.",
|
||||
identityId: "The ID of the identity to update.",
|
||||
slug: "The slug of the privilege to update.",
|
||||
newSlug: "The new slug of the privilege to update.",
|
||||
permissions: `The permission object for the privilege.
|
||||
1. [["read", "secrets", {environment: "dev", secretPath: {$glob: "/"}}]]
|
||||
2. [["read", "secrets", {environment: "dev"}], ["create", "secrets", {environment: "dev"}]]
|
||||
2. [["read", "secrets", {environment: "dev"}]]
|
||||
`,
|
||||
isPackPermission: "Whether the server should pack(compact) the permission object.",
|
||||
isTemporary: "Whether the privilege is temporary.",
|
||||
temporaryMode: "Type of temporary access given. Types: relative",
|
||||
temporaryRange: "TTL for the temporay time. Eg: 1m, 1h, 1d",
|
||||
temporaryAccessStartTime: "ISO time for which temporary access should begin."
|
||||
},
|
||||
DELETE: {
|
||||
projectSlug: "The slug of the project of the identity in.",
|
||||
identityId: "The ID of the identity to delete.",
|
||||
slug: "The slug of the privilege to delete."
|
||||
},
|
||||
GET_BY_SLUG: {
|
||||
projectSlug: "The slug of the project of the identity in.",
|
||||
identityId: "The ID of the identity to list.",
|
||||
slug: "The slug of the privilege."
|
||||
},
|
||||
LIST: {
|
||||
projectSlug: "The slug of the project of the identity in.",
|
||||
identityId: "The ID of the identity to list.",
|
||||
unpacked: "Whether the system should send the permissions as unpacked"
|
||||
}
|
||||
};
|
||||
|
||||
export const PROJECT_USER_ADDITIONAL_PRIVILEGE = {
|
||||
CREATE: {
|
||||
projectMembershipId: "Project membership id of user",
|
||||
slug: "The slug of the privilege to create.",
|
||||
permissions:
|
||||
"The permission object for the privilege. Refer https://casl.js.org/v6/en/guide/define-rules#the-shape-of-raw-rule to understand the shape",
|
||||
isPackPermission: "Whether the server should pack(compact) the permission object.",
|
||||
isTemporary: "Whether the privilege is temporary.",
|
||||
temporaryMode: "Type of temporary access given. Types: relative",
|
||||
temporaryRange: "TTL for the temporay time. Eg: 1m, 1h, 1d",
|
||||
temporaryAccessStartTime: "ISO time for which temporary access should begin."
|
||||
},
|
||||
UPDATE: {
|
||||
privilegeId: "The id of privilege object",
|
||||
slug: "The slug of the privilege to create.",
|
||||
newSlug: "The new slug of the privilege to create.",
|
||||
permissions:
|
||||
"The permission object for the privilege. Refer https://casl.js.org/v6/en/guide/define-rules#the-shape-of-raw-rule to understand the shape",
|
||||
isPackPermission: "Whether the server should pack(compact) the permission object.",
|
||||
isTemporary: "Whether the privilege is temporary.",
|
||||
temporaryMode: "Type of temporary access given. Types: relative",
|
||||
temporaryRange: "TTL for the temporay time. Eg: 1m, 1h, 1d",
|
||||
temporaryAccessStartTime: "ISO time for which temporary access should begin."
|
||||
},
|
||||
DELETE: {
|
||||
privilegeId: "The id of privilege object"
|
||||
},
|
||||
GET_BY_PRIVILEGEID: {
|
||||
privilegeId: "The id of privilege object"
|
||||
},
|
||||
LIST: {
|
||||
projectMembershipId: "Project membership id of user"
|
||||
}
|
||||
};
|
||||
|
||||
export const INTEGRATION_AUTH = {
|
||||
GET: {
|
||||
integrationAuthId: "The id of integration authentication object."
|
||||
},
|
||||
DELETE: {
|
||||
integration: "The slug of the integration to be unauthorized.",
|
||||
projectId: "The ID of the project to delete the integration auth from."
|
||||
},
|
||||
DELETE_BY_ID: {
|
||||
integrationAuthId: "The id of integration authentication object to delete."
|
||||
},
|
||||
CREATE_ACCESS_TOKEN: {
|
||||
workspaceId: "The ID of the project to create the integration auth for.",
|
||||
integration: "The slug of integration for the auth object.",
|
||||
accessId: "The unique authorized access id of the external integration provider.",
|
||||
accessToken: "The unique authorized access token of the external integration provider.",
|
||||
url: "",
|
||||
namespace: "",
|
||||
refreshToken: "The refresh token for integration authorization."
|
||||
},
|
||||
LIST_AUTHORIZATION: {
|
||||
workspaceId: "The ID of the project to list integration auths for."
|
||||
}
|
||||
};
|
||||
|
||||
export const INTEGRATION = {
|
||||
CREATE: {
|
||||
integrationAuthId: "The ID of the integration auth object to link with integration.",
|
||||
app: "The name of the external integration providers app entity that you want to sync secrets with. Used in Netlify, GitHub, Vercel integrations.",
|
||||
isActive: "Whether the integration should be active or disabled.",
|
||||
appId:
|
||||
"The ID of the external integration providers app entity that you want to sync secrets with. Used in Netlify, GitHub, Vercel integrations.",
|
||||
secretPath: "The path of the secrets to sync secrets from.",
|
||||
sourceEnvironment: "The environment to sync secret from.",
|
||||
targetEnvironment:
|
||||
"The target environment of the integration provider. Used in cloudflare pages, TeamCity, Gitlab integrations.",
|
||||
targetEnvironmentId:
|
||||
"The target environment id of the integration provider. Used in cloudflare pages, teamcity, gitlab integrations.",
|
||||
targetService:
|
||||
"The service based grouping identifier of the external provider. Used in Terraform cloud, Checkly, Railway and NorthFlank",
|
||||
targetServiceId:
|
||||
"The service based grouping identifier ID of the external provider. Used in Terraform cloud, Checkly, Railway and NorthFlank",
|
||||
owner: "External integration providers service entity owner. Used in Github.",
|
||||
path: "Path to save the synced secrets. Used by Gitlab, AWS Parameter Store, Vault",
|
||||
region: "AWS region to sync secrets to.",
|
||||
scope: "Scope of the provider. Used by Github, Qovery",
|
||||
metadata: {
|
||||
secretPrefix: "The prefix for the saved secret. Used by GCP",
|
||||
secretSuffix: "The suffix for the saved secret. Used by GCP",
|
||||
initialSyncBehavoir: "Type of syncing behavoir with the integration",
|
||||
shouldAutoRedeploy: "Used by Render to trigger auto deploy",
|
||||
secretGCPLabel: "The label for the GCP secrets"
|
||||
}
|
||||
},
|
||||
UPDATE: {
|
||||
integrationId: "The ID of the integration object.",
|
||||
app: "The name of the external integration providers app entity that you want to sync secrets with. Used in Netlify, GitHub, Vercel integrations.",
|
||||
appId:
|
||||
"The ID of the external integration providers app entity that you want to sync secrets with. Used in Netlify, GitHub, Vercel integrations.",
|
||||
isActive: "Whether the integration should be active or disabled.",
|
||||
secretPath: "The path of the secrets to sync secrets from.",
|
||||
owner: "External integration providers service entity owner. Used in Github.",
|
||||
targetEnvironment:
|
||||
"The target environment of the integration provider. Used in cloudflare pages, TeamCity, Gitlab integrations.",
|
||||
environment: "The environment to sync secrets from."
|
||||
},
|
||||
DELETE: {
|
||||
integrationId: "The ID of the integration object."
|
||||
}
|
||||
};
|
||||
|
@ -114,7 +114,8 @@ const envSchema = z
|
||||
.enum(["true", "false"])
|
||||
.transform((val) => val === "true")
|
||||
.optional(),
|
||||
INFISICAL_CLOUD: zodStrBool.default("false")
|
||||
INFISICAL_CLOUD: zodStrBool.default("false"),
|
||||
MAINTENANCE_MODE: zodStrBool.default("false")
|
||||
})
|
||||
.transform((data) => ({
|
||||
...data,
|
||||
|
@ -24,6 +24,7 @@ import { fastifyErrHandler } from "./plugins/error-handler";
|
||||
import { registerExternalNextjs } from "./plugins/external-nextjs";
|
||||
import { serializerCompiler, validatorCompiler, ZodTypeProvider } from "./plugins/fastify-zod";
|
||||
import { fastifyIp } from "./plugins/ip";
|
||||
import { maintenanceMode } from "./plugins/maintenanceMode";
|
||||
import { fastifySwagger } from "./plugins/swagger";
|
||||
import { registerRoutes } from "./routes";
|
||||
|
||||
@ -72,6 +73,8 @@ export const main = async ({ db, smtp, logger, queue, keyStore }: TMain) => {
|
||||
}
|
||||
await server.register(helmet, { contentSecurityPolicy: false });
|
||||
|
||||
await server.register(maintenanceMode);
|
||||
|
||||
await server.register(registerRoutes, { smtp, queue, db, keyStore });
|
||||
|
||||
if (appCfg.isProductionMode) {
|
||||
|
@ -18,14 +18,43 @@ export const globalRateLimiterCfg = (): RateLimitPluginOptions => {
|
||||
};
|
||||
};
|
||||
|
||||
export const authRateLimit: RateLimitOptions = {
|
||||
// GET endpoints
|
||||
export const readLimit: RateLimitOptions = {
|
||||
timeWindow: 60 * 1000,
|
||||
max: 600,
|
||||
keyGenerator: (req) => req.realIp
|
||||
};
|
||||
|
||||
export const passwordRateLimit: RateLimitOptions = {
|
||||
// POST, PATCH, PUT, DELETE endpoints
|
||||
export const writeLimit: RateLimitOptions = {
|
||||
timeWindow: 60 * 1000,
|
||||
max: 50,
|
||||
keyGenerator: (req) => req.realIp
|
||||
};
|
||||
|
||||
// special endpoints
|
||||
export const secretsLimit: RateLimitOptions = {
|
||||
// secrets, folders, secret imports
|
||||
timeWindow: 60 * 1000,
|
||||
max: 600,
|
||||
keyGenerator: (req) => req.realIp
|
||||
};
|
||||
|
||||
export const authRateLimit: RateLimitOptions = {
|
||||
timeWindow: 60 * 1000,
|
||||
max: 60,
|
||||
keyGenerator: (req) => req.realIp
|
||||
};
|
||||
|
||||
export const inviteUserRateLimit: RateLimitOptions = {
|
||||
timeWindow: 60 * 1000,
|
||||
max: 30,
|
||||
keyGenerator: (req) => req.realIp
|
||||
};
|
||||
|
||||
export const creationLimit: RateLimitOptions = {
|
||||
// identity, project, org
|
||||
timeWindow: 60 * 1000,
|
||||
max: 30,
|
||||
keyGenerator: (req) => req.realIp
|
||||
};
|
||||
|
12
backend/src/server/plugins/maintenanceMode.ts
Normal file
12
backend/src/server/plugins/maintenanceMode.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import fp from "fastify-plugin";
|
||||
|
||||
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.");
|
||||
}
|
||||
});
|
||||
});
|
@ -4,6 +4,7 @@ import SmeeClient from "smee-client";
|
||||
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { writeLimit } from "@app/server/config/rateLimiter";
|
||||
|
||||
export const registerSecretScannerGhApp = async (server: FastifyZodProvider) => {
|
||||
const probotApp = (app: Probot) => {
|
||||
@ -49,6 +50,9 @@ export const registerSecretScannerGhApp = async (server: FastifyZodProvider) =>
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
handler: async (req, res) => {
|
||||
const eventName = req.headers["x-github-event"];
|
||||
const signatureSHA256 = req.headers["x-hub-signature-256"] as string;
|
||||
|
@ -5,12 +5,22 @@ import { registerV1EERoutes } from "@app/ee/routes/v1";
|
||||
import { auditLogDALFactory } from "@app/ee/services/audit-log/audit-log-dal";
|
||||
import { auditLogQueueServiceFactory } from "@app/ee/services/audit-log/audit-log-queue";
|
||||
import { auditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-service";
|
||||
import { dynamicSecretDALFactory } from "@app/ee/services/dynamic-secret/dynamic-secret-dal";
|
||||
import { dynamicSecretServiceFactory } from "@app/ee/services/dynamic-secret/dynamic-secret-service";
|
||||
import { buildDynamicSecretProviders } from "@app/ee/services/dynamic-secret/providers";
|
||||
import { dynamicSecretLeaseDALFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-dal";
|
||||
import { dynamicSecretLeaseQueueServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue";
|
||||
import { dynamicSecretLeaseServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-service";
|
||||
import { identityProjectAdditionalPrivilegeDALFactory } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-dal";
|
||||
import { identityProjectAdditionalPrivilegeServiceFactory } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service";
|
||||
import { ldapConfigDALFactory } from "@app/ee/services/ldap-config/ldap-config-dal";
|
||||
import { ldapConfigServiceFactory } from "@app/ee/services/ldap-config/ldap-config-service";
|
||||
import { licenseDALFactory } from "@app/ee/services/license/license-dal";
|
||||
import { licenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { permissionDALFactory } from "@app/ee/services/permission/permission-dal";
|
||||
import { permissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { projectUserAdditionalPrivilegeDALFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-dal";
|
||||
import { projectUserAdditionalPrivilegeServiceFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-service";
|
||||
import { samlConfigDALFactory } from "@app/ee/services/saml-config/saml-config-dal";
|
||||
import { samlConfigServiceFactory } from "@app/ee/services/saml-config/saml-config-service";
|
||||
import { scimDALFactory } from "@app/ee/services/scim/scim-dal";
|
||||
@ -39,6 +49,7 @@ import { trustedIpServiceFactory } from "@app/ee/services/trusted-ip/trusted-ip-
|
||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { TQueueServiceFactory } from "@app/queue";
|
||||
import { readLimit } from "@app/server/config/rateLimiter";
|
||||
import { apiKeyDALFactory } from "@app/services/api-key/api-key-dal";
|
||||
import { apiKeyServiceFactory } from "@app/services/api-key/api-key-service";
|
||||
import { authDALFactory } from "@app/services/auth/auth-dal";
|
||||
@ -47,12 +58,6 @@ import { authPaswordServiceFactory } from "@app/services/auth/auth-password-serv
|
||||
import { authSignupServiceFactory } from "@app/services/auth/auth-signup-service";
|
||||
import { tokenDALFactory } from "@app/services/auth-token/auth-token-dal";
|
||||
import { tokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
||||
import { dynamicSecretDALFactory } from "@app/services/dynamic-secret/dynamic-secret-dal";
|
||||
import { dynamicSecretServiceFactory } from "@app/services/dynamic-secret/dynamic-secret-service";
|
||||
import { buildDynamicSecretProviders } from "@app/services/dynamic-secret/providers";
|
||||
import { dynamicSecretLeaseDALFactory } from "@app/services/dynamic-secret-lease/dynamic-secret-lease-dal";
|
||||
import { dynamicSecretLeaseQueueServiceFactory } from "@app/services/dynamic-secret-lease/dynamic-secret-lease-queue";
|
||||
import { dynamicSecretLeaseServiceFactory } from "@app/services/dynamic-secret-lease/dynamic-secret-lease-service";
|
||||
import { identityDALFactory } from "@app/services/identity/identity-dal";
|
||||
import { identityOrgDALFactory } from "@app/services/identity/identity-org-dal";
|
||||
import { identityServiceFactory } from "@app/services/identity/identity-service";
|
||||
@ -149,6 +154,7 @@ export const registerRoutes = async (
|
||||
|
||||
const projectDAL = projectDALFactory(db);
|
||||
const projectMembershipDAL = projectMembershipDALFactory(db);
|
||||
const projectUserAdditionalPrivilegeDAL = projectUserAdditionalPrivilegeDALFactory(db);
|
||||
const projectUserMembershipRoleDAL = projectUserMembershipRoleDALFactory(db);
|
||||
const projectRoleDAL = projectRoleDALFactory(db);
|
||||
const projectEnvDAL = projectEnvDALFactory(db);
|
||||
@ -174,6 +180,7 @@ export const registerRoutes = async (
|
||||
const identityOrgMembershipDAL = identityOrgDALFactory(db);
|
||||
const identityProjectDAL = identityProjectDALFactory(db);
|
||||
const identityProjectMembershipRoleDAL = identityProjectMembershipRoleDALFactory(db);
|
||||
const identityProjectAdditionalPrivilegeDAL = identityProjectAdditionalPrivilegeDALFactory(db);
|
||||
|
||||
const identityUaDAL = identityUaDALFactory(db);
|
||||
const identityUaClientSecretDAL = identityUaClientSecretDALFactory(db);
|
||||
@ -345,6 +352,11 @@ export const registerRoutes = async (
|
||||
projectRoleDAL,
|
||||
licenseService
|
||||
});
|
||||
const projectUserAdditionalPrivilegeService = projectUserAdditionalPrivilegeServiceFactory({
|
||||
permissionService,
|
||||
projectMembershipDAL,
|
||||
projectUserAdditionalPrivilegeDAL
|
||||
});
|
||||
const projectKeyService = projectKeyServiceFactory({
|
||||
permissionService,
|
||||
projectKeyDAL,
|
||||
@ -387,7 +399,8 @@ export const registerRoutes = async (
|
||||
folderDAL,
|
||||
licenseService,
|
||||
projectUserMembershipRoleDAL,
|
||||
identityProjectMembershipRoleDAL
|
||||
identityProjectMembershipRoleDAL,
|
||||
keyStore
|
||||
});
|
||||
|
||||
const projectEnvService = projectEnvServiceFactory({
|
||||
@ -398,7 +411,12 @@ export const registerRoutes = async (
|
||||
folderDAL
|
||||
});
|
||||
|
||||
const projectRoleService = projectRoleServiceFactory({ permissionService, projectRoleDAL });
|
||||
const projectRoleService = projectRoleServiceFactory({
|
||||
permissionService,
|
||||
projectRoleDAL,
|
||||
projectUserMembershipRoleDAL,
|
||||
identityProjectMembershipRoleDAL
|
||||
});
|
||||
|
||||
const snapshotService = secretSnapshotServiceFactory({
|
||||
permissionService,
|
||||
@ -479,6 +497,7 @@ export const registerRoutes = async (
|
||||
snapshotService,
|
||||
secretQueueService,
|
||||
secretImportDAL,
|
||||
projectEnvDAL,
|
||||
projectBotService
|
||||
});
|
||||
const sarService = secretApprovalRequestServiceFactory({
|
||||
@ -548,6 +567,12 @@ export const registerRoutes = async (
|
||||
identityProjectMembershipRoleDAL,
|
||||
projectRoleDAL
|
||||
});
|
||||
const identityProjectAdditionalPrivilegeService = identityProjectAdditionalPrivilegeServiceFactory({
|
||||
projectDAL,
|
||||
identityProjectAdditionalPrivilegeDAL,
|
||||
permissionService,
|
||||
identityProjectDAL
|
||||
});
|
||||
const identityUaService = identityUaServiceFactory({
|
||||
identityOrgMembershipDAL,
|
||||
permissionService,
|
||||
@ -638,7 +663,9 @@ export const registerRoutes = async (
|
||||
trustedIp: trustedIpService,
|
||||
scim: scimService,
|
||||
secretBlindIndex: secretBlindIndexService,
|
||||
telemetry: telemetryService
|
||||
telemetry: telemetryService,
|
||||
projectUserAdditionalPrivilege: projectUserAdditionalPrivilegeService,
|
||||
identityProjectAdditionalPrivilege: identityProjectAdditionalPrivilegeService
|
||||
});
|
||||
|
||||
server.decorate<FastifyZodProvider["store"]>("store", {
|
||||
@ -650,8 +677,11 @@ export const registerRoutes = async (
|
||||
await server.register(injectAuditLogInfo);
|
||||
|
||||
server.route({
|
||||
url: "/api/status",
|
||||
method: "GET",
|
||||
url: "/api/status",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
|
@ -3,6 +3,7 @@ import { z } from "zod";
|
||||
import { OrganizationsSchema, SuperAdminSchema, UsersSchema } from "@app/db/schemas";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { UnauthorizedError } from "@app/lib/errors";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifySuperAdmin } from "@app/server/plugins/auth/superAdmin";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
@ -11,24 +12,33 @@ import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
|
||||
|
||||
export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/config",
|
||||
method: "GET",
|
||||
url: "/config",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
config: SuperAdminSchema.omit({ createdAt: true, updatedAt: true })
|
||||
config: SuperAdminSchema.omit({ createdAt: true, updatedAt: true }).merge(
|
||||
z.object({ isMigrationModeOn: z.boolean() })
|
||||
)
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async () => {
|
||||
const config = await getServerCfg();
|
||||
return { config };
|
||||
const serverEnvs = getConfig();
|
||||
return { config: { ...config, isMigrationModeOn: serverEnvs.MAINTENANCE_MODE } };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/config",
|
||||
method: "PATCH",
|
||||
url: "/config",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
allowSignUp: z.boolean().optional(),
|
||||
@ -52,8 +62,11 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/signup",
|
||||
method: "POST",
|
||||
url: "/signup",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
email: z.string().email().trim(),
|
||||
|
@ -3,7 +3,7 @@ import { z } from "zod";
|
||||
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||
import { authRateLimit } from "@app/server/config/rateLimiter";
|
||||
import { authRateLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode, AuthModeRefreshJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -38,8 +38,11 @@ export const registerAuthRoutes = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/checkAuth",
|
||||
method: "POST",
|
||||
url: "/checkAuth",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -52,8 +55,11 @@ export const registerAuthRoutes = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/token",
|
||||
method: "POST",
|
||||
url: "/token",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
|
@ -1,13 +1,17 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { ProjectBotsSchema } from "@app/db/schemas";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerProjectBotRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/:projectId",
|
||||
method: "GET",
|
||||
url: "/:projectId",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
projectId: z.string().trim()
|
||||
@ -38,8 +42,11 @@ export const registerProjectBotRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:botId/active",
|
||||
method: "PATCH",
|
||||
url: "/:botId/active",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
isActive: z.boolean(),
|
||||
|
@ -1,11 +1,15 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { UNIVERSAL_AUTH } from "@app/lib/api-docs";
|
||||
import { writeLimit } from "@app/server/config/rateLimiter";
|
||||
|
||||
export const registerIdentityAccessTokenRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/token/renew",
|
||||
method: "POST",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Renew access token",
|
||||
body: z.object({
|
||||
|
@ -3,6 +3,7 @@ import { z } from "zod";
|
||||
import { IdentitiesSchema, OrgMembershipRole } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { IDENTITIES } from "@app/lib/api-docs";
|
||||
import { creationLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
@ -12,6 +13,9 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: creationLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
description: "Create identity",
|
||||
@ -71,6 +75,9 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "PATCH",
|
||||
url: "/:identityId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
description: "Update identity",
|
||||
@ -121,6 +128,9 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/:identityId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
description: "Delete identity",
|
||||
|
@ -3,6 +3,7 @@ import { z } from "zod";
|
||||
import { IdentityUaClientSecretsSchema, IdentityUniversalAuthsSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { UNIVERSAL_AUTH } from "@app/lib/api-docs";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
|
||||
@ -22,8 +23,11 @@ export const sanitizedClientSecretSchema = IdentityUaClientSecretsSchema.pick({
|
||||
|
||||
export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/universal-auth/login",
|
||||
method: "POST",
|
||||
url: "/universal-auth/login",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Login with Universal Auth",
|
||||
body: z.object({
|
||||
@ -66,8 +70,11 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/universal-auth/identities/:identityId",
|
||||
method: "POST",
|
||||
url: "/universal-auth/identities/:identityId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
description: "Attach Universal Auth configuration onto identity",
|
||||
@ -156,8 +163,11 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/universal-auth/identities/:identityId",
|
||||
method: "PATCH",
|
||||
url: "/universal-auth/identities/:identityId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
description: "Update Universal Auth configuration on identity",
|
||||
@ -239,8 +249,11 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/universal-auth/identities/:identityId",
|
||||
method: "GET",
|
||||
url: "/universal-auth/identities/:identityId",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
description: "Retrieve Universal Auth configuration on identity",
|
||||
@ -283,8 +296,11 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/universal-auth/identities/:identityId/client-secrets",
|
||||
method: "POST",
|
||||
url: "/universal-auth/identities/:identityId/client-secrets",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
description: "Create Universal Auth Client Secret for identity",
|
||||
@ -335,8 +351,11 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/universal-auth/identities/:identityId/client-secrets",
|
||||
method: "GET",
|
||||
url: "/universal-auth/identities/:identityId/client-secrets",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
description: "List Universal Auth Client Secrets for identity",
|
||||
@ -378,8 +397,11 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/universal-auth/identities/:identityId/client-secrets/:clientSecretId/revoke",
|
||||
method: "POST",
|
||||
url: "/universal-auth/identities/:identityId/client-secrets/:clientSecretId/revoke",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
description: "Revoke Universal Auth Client Secrets for identity",
|
||||
|
@ -1,8 +1,6 @@
|
||||
import { registerAdminRouter } from "./admin-router";
|
||||
import { registerAuthRoutes } from "./auth-router";
|
||||
import { registerProjectBotRouter } from "./bot-router";
|
||||
import { registerDynamicSecretLeaseRouter } from "./dynamic-secret-lease-router";
|
||||
import { registerDynamicSecretRouter } from "./dynamic-secret-router";
|
||||
import { registerIdentityAccessTokenRouter } from "./identity-access-token-router";
|
||||
import { registerIdentityRouter } from "./identity-router";
|
||||
import { registerIdentityUaRouter } from "./identity-ua";
|
||||
@ -54,14 +52,6 @@ export const registerV1Routes = async (server: FastifyZodProvider) => {
|
||||
{ prefix: "/workspace" }
|
||||
);
|
||||
|
||||
await server.register(
|
||||
async (dynamicSecretRouter) => {
|
||||
await dynamicSecretRouter.register(registerDynamicSecretRouter);
|
||||
await dynamicSecretRouter.register(registerDynamicSecretLeaseRouter, { prefix: "/leases" });
|
||||
},
|
||||
{ prefix: "/dynamic-secrets" }
|
||||
);
|
||||
|
||||
await server.register(registerProjectBotRouter, { prefix: "/bot" });
|
||||
await server.register(registerIntegrationRouter, { prefix: "/integration" });
|
||||
await server.register(registerIntegrationAuthRouter, { prefix: "/integration-auth" });
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { INTEGRATION_AUTH } from "@app/lib/api-docs";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -8,10 +10,19 @@ import { integrationAuthPubSchema } from "../sanitizedSchemas";
|
||||
|
||||
export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/integration-options",
|
||||
method: "GET",
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
url: "/integration-options",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
description: "List of integrations available.",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
response: {
|
||||
200: z.object({
|
||||
integrationOptions: z
|
||||
@ -36,12 +47,21 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:integrationAuthId",
|
||||
method: "GET",
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
url: "/:integrationAuthId",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
description: "Get details of an integration authorization by auth object id.",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
integrationAuthId: z.string().trim()
|
||||
integrationAuthId: z.string().trim().describe(INTEGRATION_AUTH.GET.integrationAuthId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -62,13 +82,22 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "DELETE",
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
description: "Remove all integration's auth object from the project.",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
querystring: z.object({
|
||||
integration: z.string().trim(),
|
||||
projectId: z.string().trim()
|
||||
integration: z.string().trim().describe(INTEGRATION_AUTH.DELETE.integration),
|
||||
projectId: z.string().trim().describe(INTEGRATION_AUTH.DELETE.projectId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -102,12 +131,21 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:integrationAuthId",
|
||||
method: "DELETE",
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
url: "/:integrationAuthId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
description: "Remove an integration auth object by object id.",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
integrationAuthId: z.string().trim()
|
||||
integrationAuthId: z.string().trim().describe(INTEGRATION_AUTH.DELETE_BY_ID.integrationAuthId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -140,8 +178,11 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/oauth-token",
|
||||
method: "POST",
|
||||
url: "/oauth-token",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
body: z.object({
|
||||
@ -181,18 +222,27 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/access-token",
|
||||
method: "POST",
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
url: "/access-token",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
description: "Create the integration authentication object required for syncing secrets.",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
body: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
integration: z.string().trim(),
|
||||
accessId: z.string().trim().optional(),
|
||||
accessToken: z.string().trim().optional(),
|
||||
url: z.string().url().trim().optional(),
|
||||
namespace: z.string().trim().optional(),
|
||||
refreshToken: z.string().trim().optional()
|
||||
workspaceId: z.string().trim().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.workspaceId),
|
||||
integration: z.string().trim().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.integration),
|
||||
accessId: z.string().trim().optional().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.accessId),
|
||||
accessToken: z.string().trim().optional().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.accessToken),
|
||||
url: z.string().url().trim().optional().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.url),
|
||||
namespace: z.string().trim().optional().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.namespace),
|
||||
refreshToken: z.string().trim().optional().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.refreshToken)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -225,8 +275,11 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:integrationAuthId/apps",
|
||||
method: "GET",
|
||||
url: "/:integrationAuthId/apps",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
@ -262,8 +315,11 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:integrationAuthId/teams",
|
||||
method: "GET",
|
||||
url: "/:integrationAuthId/teams",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
@ -293,8 +349,11 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:integrationAuthId/vercel/branches",
|
||||
method: "GET",
|
||||
url: "/:integrationAuthId/vercel/branches",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
@ -323,8 +382,11 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:integrationAuthId/checkly/groups",
|
||||
method: "GET",
|
||||
url: "/:integrationAuthId/checkly/groups",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
@ -353,8 +415,11 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:integrationAuthId/github/orgs",
|
||||
method: "GET",
|
||||
url: "/:integrationAuthId/github/orgs",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
@ -381,8 +446,11 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:integrationAuthId/github/envs",
|
||||
method: "GET",
|
||||
url: "/:integrationAuthId/github/envs",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
@ -415,8 +483,11 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:integrationAuthId/qovery/orgs",
|
||||
method: "GET",
|
||||
url: "/:integrationAuthId/qovery/orgs",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
@ -441,8 +512,11 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:integrationAuthId/qovery/projects",
|
||||
method: "GET",
|
||||
url: "/:integrationAuthId/qovery/projects",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
@ -471,8 +545,11 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:integrationAuthId/qovery/environments",
|
||||
method: "GET",
|
||||
url: "/:integrationAuthId/qovery/environments",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
@ -501,8 +578,11 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:integrationAuthId/qovery/apps",
|
||||
method: "GET",
|
||||
url: "/:integrationAuthId/qovery/apps",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
@ -531,8 +611,11 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:integrationAuthId/qovery/containers",
|
||||
method: "GET",
|
||||
url: "/:integrationAuthId/qovery/containers",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
@ -561,8 +644,11 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:integrationAuthId/qovery/jobs",
|
||||
method: "GET",
|
||||
url: "/:integrationAuthId/qovery/jobs",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
@ -591,8 +677,11 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:integrationAuthId/heroku/pipelines",
|
||||
method: "GET",
|
||||
url: "/:integrationAuthId/heroku/pipelines",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
@ -623,8 +712,11 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:integrationAuthId/railway/environments",
|
||||
method: "GET",
|
||||
url: "/:integrationAuthId/railway/environments",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
@ -653,8 +745,11 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:integrationAuthId/railway/services",
|
||||
method: "GET",
|
||||
url: "/:integrationAuthId/railway/services",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
@ -683,8 +778,11 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:integrationAuthId/bitbucket/workspaces",
|
||||
method: "GET",
|
||||
url: "/:integrationAuthId/bitbucket/workspaces",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
@ -719,8 +817,11 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:integrationAuthId/northflank/secret-groups",
|
||||
method: "GET",
|
||||
url: "/:integrationAuthId/northflank/secret-groups",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
@ -754,8 +855,11 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:integrationAuthId/teamcity/build-configs",
|
||||
method: "GET",
|
||||
url: "/:integrationAuthId/teamcity/build-configs",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
|
@ -2,7 +2,9 @@ import { z } from "zod";
|
||||
|
||||
import { IntegrationsSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { INTEGRATION } from "@app/lib/api-docs";
|
||||
import { removeTrailingSlash, shake } from "@app/lib/fn";
|
||||
import { writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
@ -10,36 +12,51 @@ import { PostHogEventTypes, TIntegrationCreatedEvent } from "@app/services/telem
|
||||
|
||||
export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "POST",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Create an integration to sync secrets.",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
body: z.object({
|
||||
integrationAuthId: z.string().trim(),
|
||||
app: z.string().trim().optional(),
|
||||
isActive: z.boolean(),
|
||||
appId: z.string().trim().optional(),
|
||||
secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||
sourceEnvironment: z.string().trim(),
|
||||
targetEnvironment: z.string().trim().optional(),
|
||||
targetEnvironmentId: z.string().trim().optional(),
|
||||
targetService: z.string().trim().optional(),
|
||||
targetServiceId: z.string().trim().optional(),
|
||||
owner: z.string().trim().optional(),
|
||||
path: z.string().trim().optional(),
|
||||
region: z.string().trim().optional(),
|
||||
scope: z.string().trim().optional(),
|
||||
integrationAuthId: z.string().trim().describe(INTEGRATION.CREATE.integrationAuthId),
|
||||
app: z.string().trim().optional().describe(INTEGRATION.CREATE.app),
|
||||
isActive: z.boolean().describe(INTEGRATION.CREATE.isActive).default(true),
|
||||
appId: z.string().trim().optional().describe(INTEGRATION.CREATE.appId),
|
||||
secretPath: z
|
||||
.string()
|
||||
.trim()
|
||||
.default("/")
|
||||
.transform(removeTrailingSlash)
|
||||
.describe(INTEGRATION.CREATE.secretPath),
|
||||
sourceEnvironment: z.string().trim().describe(INTEGRATION.CREATE.sourceEnvironment),
|
||||
targetEnvironment: z.string().trim().optional().describe(INTEGRATION.CREATE.targetEnvironment),
|
||||
targetEnvironmentId: z.string().trim().optional().describe(INTEGRATION.CREATE.targetEnvironmentId),
|
||||
targetService: z.string().trim().optional().describe(INTEGRATION.CREATE.targetService),
|
||||
targetServiceId: z.string().trim().optional().describe(INTEGRATION.CREATE.targetServiceId),
|
||||
owner: z.string().trim().optional().describe(INTEGRATION.CREATE.owner),
|
||||
path: z.string().trim().optional().describe(INTEGRATION.CREATE.path),
|
||||
region: z.string().trim().optional().describe(INTEGRATION.CREATE.region),
|
||||
scope: z.string().trim().optional().describe(INTEGRATION.CREATE.scope),
|
||||
metadata: z
|
||||
.object({
|
||||
secretPrefix: z.string().optional(),
|
||||
secretSuffix: z.string().optional(),
|
||||
initialSyncBehavior: z.string().optional(),
|
||||
shouldAutoRedeploy: z.boolean().optional(),
|
||||
secretPrefix: z.string().optional().describe(INTEGRATION.CREATE.metadata.secretPrefix),
|
||||
secretSuffix: z.string().optional().describe(INTEGRATION.CREATE.metadata.secretSuffix),
|
||||
initialSyncBehavior: z.string().optional().describe(INTEGRATION.CREATE.metadata.initialSyncBehavoir),
|
||||
shouldAutoRedeploy: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldAutoRedeploy),
|
||||
secretGCPLabel: z
|
||||
.object({
|
||||
labelName: z.string(),
|
||||
labelValue: z.string()
|
||||
})
|
||||
.optional()
|
||||
.describe(INTEGRATION.CREATE.metadata.secretGCPLabel)
|
||||
})
|
||||
.optional()
|
||||
}),
|
||||
@ -49,7 +66,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { integration, integrationAuth } = await server.services.integration.createIntegration({
|
||||
actorId: req.permission.id,
|
||||
@ -99,20 +116,34 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:integrationId",
|
||||
method: "PATCH",
|
||||
url: "/:integrationId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Update an integration by integration id",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
integrationId: z.string().trim()
|
||||
integrationId: z.string().trim().describe(INTEGRATION.UPDATE.integrationId)
|
||||
}),
|
||||
body: z.object({
|
||||
app: z.string().trim(),
|
||||
appId: z.string().trim(),
|
||||
isActive: z.boolean(),
|
||||
secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||
targetEnvironment: z.string().trim(),
|
||||
owner: z.string().trim(),
|
||||
environment: z.string().trim()
|
||||
app: z.string().trim().describe(INTEGRATION.UPDATE.app),
|
||||
appId: z.string().trim().describe(INTEGRATION.UPDATE.appId),
|
||||
isActive: z.boolean().describe(INTEGRATION.UPDATE.isActive),
|
||||
secretPath: z
|
||||
.string()
|
||||
.trim()
|
||||
.default("/")
|
||||
.transform(removeTrailingSlash)
|
||||
.describe(INTEGRATION.UPDATE.secretPath),
|
||||
targetEnvironment: z.string().trim().describe(INTEGRATION.UPDATE.targetEnvironment),
|
||||
owner: z.string().trim().describe(INTEGRATION.UPDATE.owner),
|
||||
environment: z.string().trim().describe(INTEGRATION.UPDATE.environment)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -120,7 +151,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const integration = await server.services.integration.updateIntegration({
|
||||
actorId: req.permission.id,
|
||||
@ -135,11 +166,20 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:integrationId",
|
||||
method: "DELETE",
|
||||
url: "/:integrationId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Remove an integration using the integration object ID",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
integrationId: z.string().trim()
|
||||
integrationId: z.string().trim().describe(INTEGRATION.DELETE.integrationId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -147,7 +187,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const integration = await server.services.integration.deleteIntegration({
|
||||
actorId: req.permission.id,
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { UsersSchema } from "@app/db/schemas";
|
||||
import { inviteUserRateLimit } from "@app/server/config/rateLimiter";
|
||||
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
|
||||
@ -9,6 +10,9 @@ import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
|
||||
export const registerInviteOrgRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/signup",
|
||||
config: {
|
||||
rateLimit: inviteUserRateLimit
|
||||
},
|
||||
method: "POST",
|
||||
schema: {
|
||||
body: z.object({
|
||||
@ -52,6 +56,9 @@ export const registerInviteOrgRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/verify",
|
||||
method: "POST",
|
||||
config: {
|
||||
rateLimit: inviteUserRateLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
email: z.string().trim().email(),
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { IncidentContactsSchema, OrganizationsSchema, OrgMembershipsSchema, UsersSchema } from "@app/db/schemas";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -8,6 +9,9 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -25,6 +29,9 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:organizationId",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
organizationId: z.string().trim()
|
||||
@ -50,6 +57,9 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:organizationId/users",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
organizationId: z.string().trim()
|
||||
@ -87,6 +97,9 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "PATCH",
|
||||
url: "/:organizationId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({ organizationId: z.string().trim() }),
|
||||
body: z.object({
|
||||
@ -128,6 +141,9 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:organizationId/incidentContactOrg",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({ organizationId: z.string().trim() }),
|
||||
response: {
|
||||
@ -151,6 +167,9 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/:organizationId/incidentContactOrg",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({ organizationId: z.string().trim() }),
|
||||
body: z.object({ email: z.string().email().trim() }),
|
||||
@ -176,6 +195,9 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/:organizationId/incidentContactOrg/:incidentContactId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({ organizationId: z.string().trim(), incidentContactId: z.string().trim() }),
|
||||
response: {
|
||||
|
@ -2,7 +2,7 @@ import { z } from "zod";
|
||||
|
||||
import { BackupPrivateKeySchema, UsersSchema } from "@app/db/schemas";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { passwordRateLimit } from "@app/server/config/rateLimiter";
|
||||
import { authRateLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { validateSignUpAuthorization } from "@app/services/auth/auth-fns";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
@ -12,7 +12,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => {
|
||||
method: "POST",
|
||||
url: "/srp1",
|
||||
config: {
|
||||
rateLimit: passwordRateLimit
|
||||
rateLimit: authRateLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
@ -39,7 +39,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => {
|
||||
method: "POST",
|
||||
url: "/change-password",
|
||||
config: {
|
||||
rateLimit: passwordRateLimit
|
||||
rateLimit: authRateLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
@ -78,7 +78,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => {
|
||||
method: "POST",
|
||||
url: "/email/password-reset",
|
||||
config: {
|
||||
rateLimit: passwordRateLimit
|
||||
rateLimit: authRateLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
@ -103,7 +103,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => {
|
||||
method: "POST",
|
||||
url: "/email/password-reset-verify",
|
||||
config: {
|
||||
rateLimit: passwordRateLimit
|
||||
rateLimit: authRateLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
@ -133,7 +133,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => {
|
||||
method: "POST",
|
||||
url: "/backup-private-key",
|
||||
config: {
|
||||
rateLimit: passwordRateLimit
|
||||
rateLimit: authRateLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
@ -168,7 +168,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => {
|
||||
method: "GET",
|
||||
url: "/backup-private-key",
|
||||
config: {
|
||||
rateLimit: passwordRateLimit
|
||||
rateLimit: authRateLimit
|
||||
},
|
||||
schema: {
|
||||
response: {
|
||||
@ -190,6 +190,9 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/password-reset",
|
||||
config: {
|
||||
rateLimit: authRateLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
protectedKey: z.string().trim(),
|
||||
|
@ -3,13 +3,17 @@ import { z } from "zod";
|
||||
import { ProjectEnvironmentsSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { ENVIRONMENTS } from "@app/lib/api-docs";
|
||||
import { writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/:workspaceId/environments",
|
||||
method: "POST",
|
||||
url: "/:workspaceId/environments",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Create environment",
|
||||
security: [
|
||||
@ -64,8 +68,11 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:workspaceId/environments/:id",
|
||||
method: "PATCH",
|
||||
url: "/:workspaceId/environments/:id",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Update environment",
|
||||
security: [
|
||||
@ -128,8 +135,11 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:workspaceId/environments/:id",
|
||||
method: "DELETE",
|
||||
url: "/:workspaceId/environments/:id",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Delete environment",
|
||||
security: [
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -7,6 +8,9 @@ export const registerProjectKeyRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/:workspaceId/key",
|
||||
method: "POST",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
|
@ -10,14 +10,18 @@ import {
|
||||
} from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { PROJECTS } from "@app/lib/api-docs";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { ProjectUserMembershipTemporaryMode } from "@app/services/project-membership/project-membership-types";
|
||||
|
||||
export const registerProjectMembershipRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/:workspaceId/memberships",
|
||||
method: "GET",
|
||||
url: "/:workspaceId/memberships",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Return project user memberships",
|
||||
security: [
|
||||
@ -75,8 +79,11 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:workspaceId/memberships",
|
||||
method: "POST",
|
||||
url: "/:workspaceId/memberships",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
@ -126,8 +133,11 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:workspaceId/memberships/:membershipId",
|
||||
method: "PATCH",
|
||||
url: "/:workspaceId/memberships/:membershipId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Update project user membership",
|
||||
security: [
|
||||
@ -158,7 +168,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
||||
])
|
||||
)
|
||||
.min(1)
|
||||
.refine((data) => data.some(({ isTemporary }) => !isTemporary), "At least long lived role is required")
|
||||
.refine((data) => data.some(({ isTemporary }) => !isTemporary), "At least one long lived role is required")
|
||||
.describe(PROJECTS.UPDATE_USER_MEMBERSHIP.roles)
|
||||
}),
|
||||
response: {
|
||||
@ -197,8 +207,11 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:workspaceId/memberships/:membershipId",
|
||||
method: "DELETE",
|
||||
url: "/:workspaceId/memberships/:membershipId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Delete project user membership",
|
||||
security: [
|
||||
|
@ -7,7 +7,8 @@ import {
|
||||
UserEncryptionKeysSchema,
|
||||
UsersSchema
|
||||
} from "@app/db/schemas";
|
||||
import { PROJECTS } from "@app/lib/api-docs";
|
||||
import { INTEGRATION_AUTH, PROJECTS } from "@app/lib/api-docs";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { ProjectFilterType } from "@app/services/project/project-types";
|
||||
@ -24,8 +25,11 @@ const projectWithEnv = ProjectsSchema.merge(
|
||||
|
||||
export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/:workspaceId/keys",
|
||||
method: "GET",
|
||||
url: "/:workspaceId/keys",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
@ -55,8 +59,11 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:workspaceId/users",
|
||||
method: "GET",
|
||||
url: "/:workspaceId/users",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
@ -108,8 +115,11 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "GET",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -125,8 +135,11 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:workspaceId",
|
||||
method: "GET",
|
||||
url: "/:workspaceId",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim().describe(PROJECTS.GET.workspaceId)
|
||||
@ -154,8 +167,11 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:workspaceId",
|
||||
method: "DELETE",
|
||||
url: "/:workspaceId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim().describe(PROJECTS.DELETE.workspaceId)
|
||||
@ -185,6 +201,9 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/:workspaceId/name",
|
||||
method: "POST",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
@ -217,8 +236,11 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:workspaceId",
|
||||
method: "PATCH",
|
||||
url: "/:workspaceId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim().describe(PROJECTS.UPDATE.workspaceId)
|
||||
@ -261,8 +283,11 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:workspaceId/auto-capitalization",
|
||||
method: "POST",
|
||||
url: "/:workspaceId/auto-capitalization",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
@ -295,8 +320,11 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:workspaceId/integrations",
|
||||
method: "GET",
|
||||
url: "/:workspaceId/integrations",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
@ -329,11 +357,20 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:workspaceId/authorizations",
|
||||
method: "GET",
|
||||
url: "/:workspaceId/authorizations",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: "List integration auth objects for a workspace.",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
workspaceId: z.string().trim().describe(INTEGRATION_AUTH.LIST_AUTHORIZATION.workspaceId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -341,7 +378,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const authorizations = await server.services.integrationAuth.listIntegrationAuthByProjectId({
|
||||
actorId: req.permission.id,
|
||||
@ -355,8 +392,11 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:workspaceId/service-token-data",
|
||||
method: "GET",
|
||||
url: "/:workspaceId/service-token-data",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
|
@ -4,6 +4,7 @@ import { SecretFoldersSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { FOLDERS } from "@app/lib/api-docs";
|
||||
import { removeTrailingSlash } from "@app/lib/fn";
|
||||
import { readLimit, secretsLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -11,6 +12,9 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "POST",
|
||||
config: {
|
||||
rateLimit: secretsLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Create folders",
|
||||
security: [
|
||||
@ -65,6 +69,9 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
server.route({
|
||||
url: "/:folderId",
|
||||
method: "PATCH",
|
||||
config: {
|
||||
rateLimit: secretsLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Update folder",
|
||||
security: [
|
||||
@ -124,8 +131,11 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
|
||||
// TODO(daniel): Expose this route in api reference and write docs for it.
|
||||
server.route({
|
||||
url: "/:folderIdOrName",
|
||||
method: "DELETE",
|
||||
url: "/:folderIdOrName",
|
||||
config: {
|
||||
rateLimit: secretsLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Delete a folder",
|
||||
security: [
|
||||
@ -181,8 +191,11 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "GET",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Get folders",
|
||||
security: [
|
||||
|
@ -4,13 +4,17 @@ import { SecretImportsSchema, SecretsSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { SECRET_IMPORTS } from "@app/lib/api-docs";
|
||||
import { removeTrailingSlash } from "@app/lib/fn";
|
||||
import { readLimit, secretsLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerSecretImportRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "POST",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: secretsLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Create secret imports",
|
||||
security: [
|
||||
@ -71,8 +75,11 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:secretImportId",
|
||||
method: "PATCH",
|
||||
url: "/:secretImportId",
|
||||
config: {
|
||||
rateLimit: secretsLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Update secret imports",
|
||||
security: [
|
||||
@ -143,8 +150,11 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:secretImportId",
|
||||
method: "DELETE",
|
||||
url: "/:secretImportId",
|
||||
config: {
|
||||
rateLimit: secretsLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Delete secret imports",
|
||||
security: [
|
||||
@ -204,8 +214,11 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "GET",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Get secret imports",
|
||||
security: [
|
||||
@ -262,6 +275,9 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
|
||||
server.route({
|
||||
url: "/secrets",
|
||||
method: "GET",
|
||||
config: {
|
||||
rateLimit: secretsLimit
|
||||
},
|
||||
schema: {
|
||||
querystring: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
|
@ -2,13 +2,17 @@ import { z } from "zod";
|
||||
|
||||
import { SecretTagsSchema } from "@app/db/schemas";
|
||||
import { SECRET_TAGS } from "@app/lib/api-docs";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerSecretTagRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/:projectId/tags",
|
||||
method: "GET",
|
||||
url: "/:projectId/tags",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
projectId: z.string().trim().describe(SECRET_TAGS.LIST.projectId)
|
||||
@ -33,8 +37,11 @@ export const registerSecretTagRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:projectId/tags",
|
||||
method: "POST",
|
||||
url: "/:projectId/tags",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
projectId: z.string().trim().describe(SECRET_TAGS.CREATE.projectId)
|
||||
@ -65,8 +72,11 @@ export const registerSecretTagRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:projectId/tags/:tagId",
|
||||
method: "DELETE",
|
||||
url: "/:projectId/tags/:tagId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
projectId: z.string().trim().describe(SECRET_TAGS.DELETE.projectId),
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { UserActionsSchema } from "@app/db/schemas";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -8,6 +9,9 @@ export const registerUserActionRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "POST",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
action: z.string().trim()
|
||||
@ -29,6 +33,9 @@ export const registerUserActionRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "GET",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
querystring: z.object({
|
||||
action: z.string().trim()
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { UserEncryptionKeysSchema, UsersSchema } from "@app/db/schemas";
|
||||
import { readLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -8,6 +9,9 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
|
@ -3,6 +3,7 @@ import { z } from "zod";
|
||||
import { WebhooksSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { removeTrailingSlash } from "@app/lib/fn";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -27,6 +28,9 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
body: z.object({
|
||||
@ -75,6 +79,9 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "PATCH",
|
||||
url: "/:webhookId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
@ -122,6 +129,9 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/:webhookId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
@ -159,6 +169,9 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/:webhookId/test",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
@ -186,6 +199,9 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
querystring: z.object({
|
||||
|
@ -2,6 +2,7 @@ import { z } from "zod";
|
||||
|
||||
import { IdentitiesSchema, IdentityOrgMembershipsSchema, OrgRolesSchema } from "@app/db/schemas";
|
||||
import { ORGANIZATIONS } from "@app/lib/api-docs";
|
||||
import { readLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -9,6 +10,9 @@ export const registerIdentityOrgRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:orgId/identity-memberships",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
description: "Return organization identity memberships",
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
ProjectUserMembershipRolesSchema
|
||||
} from "@app/db/schemas";
|
||||
import { PROJECTS } from "@app/lib/api-docs";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { ProjectUserMembershipTemporaryMode } from "@app/services/project-membership/project-membership-types";
|
||||
@ -16,6 +17,9 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/:projectId/identity-memberships/:identityId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
@ -48,6 +52,9 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
|
||||
server.route({
|
||||
method: "PATCH",
|
||||
url: "/:projectId/identity-memberships/:identityId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
description: "Update project identity memberships",
|
||||
@ -103,6 +110,9 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/:projectId/identity-memberships/:identityId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
description: "Delete project identity memberships",
|
||||
@ -137,6 +147,9 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:projectId/identity-memberships",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
description: "Return project identity memberships",
|
||||
|
@ -2,6 +2,7 @@ import jwt from "jsonwebtoken";
|
||||
import { z } from "zod";
|
||||
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { AuthModeMfaJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerMfaRouter = async (server: FastifyZodProvider) => {
|
||||
@ -30,8 +31,11 @@ export const registerMfaRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/mfa/send",
|
||||
method: "POST",
|
||||
url: "/mfa/send",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -48,6 +52,9 @@ export const registerMfaRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/mfa/verify",
|
||||
method: "POST",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
mfaToken: z.string().trim()
|
||||
|
@ -2,6 +2,7 @@ import { z } from "zod";
|
||||
|
||||
import { OrganizationsSchema, OrgMembershipsSchema, UserEncryptionKeysSchema, UsersSchema } from "@app/db/schemas";
|
||||
import { ORGANIZATIONS } from "@app/lib/api-docs";
|
||||
import { creationLimit, readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -9,6 +10,9 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:organizationId/memberships",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Return organization user memberships",
|
||||
security: [
|
||||
@ -55,6 +59,9 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:organizationId/workspaces",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Return projects in organization that user is part of",
|
||||
security: [
|
||||
@ -101,6 +108,9 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "PATCH",
|
||||
url: "/:organizationId/memberships/:membershipId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Update organization user memberships",
|
||||
security: [
|
||||
@ -141,6 +151,9 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/:organizationId/memberships/:membershipId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Delete organization user memberships",
|
||||
security: [
|
||||
@ -177,6 +190,9 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: creationLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
name: z.string().trim()
|
||||
@ -204,6 +220,9 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/:organizationId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
organizationId: z.string().trim()
|
||||
|
@ -3,6 +3,7 @@ import { z } from "zod";
|
||||
import { ProjectMembershipsSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { PROJECTS } from "@app/lib/api-docs";
|
||||
import { writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -10,6 +11,9 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/:projectId/memberships",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
projectId: z.string().describe(PROJECTS.INVITE_MEMBER.projectId)
|
||||
@ -56,6 +60,9 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/:projectId/memberships",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
projectId: z.string().describe(PROJECTS.REMOVE_MEMBER.projectId)
|
||||
|
@ -4,7 +4,7 @@ import { z } from "zod";
|
||||
import { ProjectKeysSchema, ProjectsSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { PROJECTS } from "@app/lib/api-docs";
|
||||
import { authRateLimit } from "@app/server/config/rateLimiter";
|
||||
import { creationLimit, readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
@ -29,8 +29,11 @@ const slugSchema = z
|
||||
export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
/* Get project key */
|
||||
server.route({
|
||||
url: "/:workspaceId/encrypted-key",
|
||||
method: "GET",
|
||||
url: "/:workspaceId/encrypted-key",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Return encrypted project key",
|
||||
security: [
|
||||
@ -78,8 +81,11 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
|
||||
/* Start upgrade of a project */
|
||||
server.route({
|
||||
url: "/:projectId/upgrade",
|
||||
method: "POST",
|
||||
url: "/:projectId/upgrade",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
projectId: z.string().trim()
|
||||
@ -108,6 +114,9 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/:projectId/upgrade/status",
|
||||
method: "GET",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
projectId: z.string().trim()
|
||||
@ -137,7 +146,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
method: "POST",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: authRateLimit
|
||||
rateLimit: creationLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
@ -187,6 +196,9 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/:slug",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
slug: slugSchema.describe("The slug of the project to delete.")
|
||||
@ -218,6 +230,9 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:slug",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
slug: slugSchema.describe("The slug of the project to get.")
|
||||
@ -248,6 +263,9 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "PATCH",
|
||||
url: "/:slug",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
slug: slugSchema.describe("The slug of the project to update.")
|
||||
|
@ -3,6 +3,7 @@ import { z } from "zod";
|
||||
import { ServiceTokensSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { removeTrailingSlash } from "@app/lib/fn";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -17,8 +18,11 @@ export const sanitizedServiceTokenSchema = ServiceTokensSchema.omit({
|
||||
|
||||
export const registerServiceTokenRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "GET",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.SERVICE_TOKEN]),
|
||||
schema: {
|
||||
description: "Return Infisical Token data",
|
||||
@ -69,8 +73,11 @@ export const registerServiceTokenRouter = async (server: FastifyZodProvider) =>
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "POST",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
body: z.object({
|
||||
@ -122,8 +129,11 @@ export const registerServiceTokenRouter = async (server: FastifyZodProvider) =>
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:serviceTokenId",
|
||||
method: "DELETE",
|
||||
url: "/:serviceTokenId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
params: z.object({
|
||||
|
@ -2,13 +2,17 @@ import { z } from "zod";
|
||||
|
||||
import { AuthTokenSessionsSchema, OrganizationsSchema, UserEncryptionKeysSchema, UsersSchema } from "@app/db/schemas";
|
||||
import { ApiKeysSchema } from "@app/db/schemas/api-keys";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMethod, AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerUserRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/me/mfa",
|
||||
method: "PATCH",
|
||||
url: "/me/mfa",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
isMfaEnabled: z.boolean()
|
||||
@ -27,8 +31,11 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/me/name",
|
||||
method: "PATCH",
|
||||
url: "/me/name",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
firstName: z.string().trim(),
|
||||
@ -48,8 +55,11 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/me/auth-methods",
|
||||
method: "PUT",
|
||||
url: "/me/auth-methods",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
authMethods: z.nativeEnum(AuthMethod).array().min(1)
|
||||
@ -70,6 +80,9 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/me/organizations",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Return organizations that current user is part of",
|
||||
security: [
|
||||
@ -93,6 +106,9 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/me/api-keys",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
response: {
|
||||
200: ApiKeysSchema.omit({ secretHash: true }).array()
|
||||
@ -108,6 +124,9 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/me/api-keys",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
name: z.string().trim(),
|
||||
@ -130,6 +149,9 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/me/api-keys/:apiKeyDataId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
apiKeyDataId: z.string().trim()
|
||||
@ -150,6 +172,9 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/me/sessions",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
response: {
|
||||
200: AuthTokenSessionsSchema.array()
|
||||
@ -165,6 +190,9 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/me/sessions",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -184,6 +212,9 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/me",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Retrieve the current user on the request",
|
||||
security: [
|
||||
@ -207,6 +238,9 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/me",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
|
@ -1,13 +1,17 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { SecretsSchema } from "@app/db/schemas";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerSecretBlindIndexRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/:projectId/secrets/blind-index-status",
|
||||
method: "GET",
|
||||
url: "/:projectId/secrets/blind-index-status",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
projectId: z.string().trim()
|
||||
@ -30,8 +34,11 @@ export const registerSecretBlindIndexRouter = async (server: FastifyZodProvider)
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:projectId/secrets",
|
||||
method: "GET",
|
||||
url: "/:projectId/secrets",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
projectId: z.string().trim()
|
||||
@ -63,8 +70,11 @@ export const registerSecretBlindIndexRouter = async (server: FastifyZodProvider)
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:projectId/secrets/names",
|
||||
method: "POST",
|
||||
url: "/:projectId/secrets/names",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
projectId: z.string().trim()
|
||||
|
@ -13,6 +13,7 @@ import { CommitType } from "@app/ee/services/secret-approval-request/secret-appr
|
||||
import { RAW_SECRETS, SECRETS } from "@app/lib/api-docs";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { removeTrailingSlash } from "@app/lib/fn";
|
||||
import { secretsLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
||||
import { getUserAgentType } from "@app/server/plugins/audit-log";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
@ -24,8 +25,11 @@ import { secretRawSchema } from "../sanitizedSchemas";
|
||||
|
||||
export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/tags/:secretName",
|
||||
method: "POST",
|
||||
url: "/tags/:secretName",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Attach tags to a secret",
|
||||
security: [
|
||||
@ -83,8 +87,11 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/tags/:secretName",
|
||||
method: "DELETE",
|
||||
url: "/tags/:secretName",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Detach tags from a secret",
|
||||
security: [
|
||||
@ -142,8 +149,11 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/raw",
|
||||
method: "GET",
|
||||
url: "/raw",
|
||||
config: {
|
||||
rateLimit: secretsLimit
|
||||
},
|
||||
schema: {
|
||||
description: "List secrets",
|
||||
security: [
|
||||
@ -157,6 +167,11 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
workspaceSlug: z.string().trim().optional().describe(RAW_SECRETS.LIST.workspaceSlug),
|
||||
environment: z.string().trim().optional().describe(RAW_SECRETS.LIST.environment),
|
||||
secretPath: z.string().trim().default("/").transform(removeTrailingSlash).describe(RAW_SECRETS.LIST.secretPath),
|
||||
recursive: z
|
||||
.enum(["true", "false"])
|
||||
.default("false")
|
||||
.transform((value) => value === "true")
|
||||
.describe(RAW_SECRETS.LIST.recursive),
|
||||
include_imports: z
|
||||
.enum(["true", "false"])
|
||||
.default("false")
|
||||
@ -165,7 +180,11 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
secrets: secretRawSchema.array(),
|
||||
secrets: secretRawSchema
|
||||
.extend({
|
||||
secretPath: z.string().optional()
|
||||
})
|
||||
.array(),
|
||||
imports: z
|
||||
.object({
|
||||
secretPath: z.string(),
|
||||
@ -218,7 +237,8 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
projectId: workspaceId,
|
||||
path: secretPath,
|
||||
includeImports: req.query.include_imports
|
||||
includeImports: req.query.include_imports,
|
||||
recursive: req.query.recursive
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
@ -251,8 +271,11 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/raw/:secretName",
|
||||
method: "GET",
|
||||
url: "/raw/:secretName",
|
||||
config: {
|
||||
rateLimit: secretsLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Get a secret by name",
|
||||
security: [
|
||||
@ -343,8 +366,11 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/raw/:secretName",
|
||||
method: "POST",
|
||||
url: "/raw/:secretName",
|
||||
config: {
|
||||
rateLimit: secretsLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Create secret",
|
||||
security: [
|
||||
@ -429,8 +455,11 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/raw/:secretName",
|
||||
method: "PATCH",
|
||||
url: "/raw/:secretName",
|
||||
config: {
|
||||
rateLimit: secretsLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Update secret",
|
||||
security: [
|
||||
@ -512,8 +541,11 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/raw/:secretName",
|
||||
method: "DELETE",
|
||||
url: "/raw/:secretName",
|
||||
config: {
|
||||
rateLimit: secretsLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Delete secret",
|
||||
security: [
|
||||
@ -589,13 +621,20 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "GET",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: secretsLimit
|
||||
},
|
||||
schema: {
|
||||
querystring: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
environment: z.string().trim(),
|
||||
secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||
recursive: z
|
||||
.enum(["true", "false"])
|
||||
.default("false")
|
||||
.transform((value) => value === "true"),
|
||||
include_imports: z
|
||||
.enum(["true", "false"])
|
||||
.default("false")
|
||||
@ -604,19 +643,18 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
response: {
|
||||
200: z.object({
|
||||
secrets: SecretsSchema.omit({ secretBlindIndex: true })
|
||||
.merge(
|
||||
z.object({
|
||||
_id: z.string(),
|
||||
workspace: z.string(),
|
||||
environment: z.string(),
|
||||
tags: SecretTagsSchema.pick({
|
||||
id: true,
|
||||
slug: true,
|
||||
name: true,
|
||||
color: true
|
||||
}).array()
|
||||
})
|
||||
)
|
||||
.extend({
|
||||
_id: z.string(),
|
||||
workspace: z.string(),
|
||||
environment: z.string(),
|
||||
secretPath: z.string().optional(),
|
||||
tags: SecretTagsSchema.pick({
|
||||
id: true,
|
||||
slug: true,
|
||||
name: true,
|
||||
color: true
|
||||
}).array()
|
||||
})
|
||||
.array(),
|
||||
imports: z
|
||||
.object({
|
||||
@ -648,7 +686,8 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
environment: req.query.environment,
|
||||
projectId: req.query.workspaceId,
|
||||
path: req.query.secretPath,
|
||||
includeImports: req.query.include_imports
|
||||
includeImports: req.query.include_imports,
|
||||
recursive: req.query.recursive
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
@ -697,8 +736,11 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:secretName",
|
||||
method: "GET",
|
||||
url: "/:secretName",
|
||||
config: {
|
||||
rateLimit: secretsLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
secretName: z.string().trim()
|
||||
@ -775,6 +817,9 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/:secretName",
|
||||
method: "POST",
|
||||
config: {
|
||||
rateLimit: secretsLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
@ -941,8 +986,11 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:secretName",
|
||||
method: "PATCH",
|
||||
url: "/:secretName",
|
||||
config: {
|
||||
rateLimit: secretsLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
secretName: z.string()
|
||||
@ -1125,8 +1173,11 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/:secretName",
|
||||
method: "DELETE",
|
||||
url: "/:secretName",
|
||||
config: {
|
||||
rateLimit: secretsLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
secretName: z.string()
|
||||
@ -1246,8 +1297,11 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/batch",
|
||||
method: "POST",
|
||||
url: "/batch",
|
||||
config: {
|
||||
rateLimit: secretsLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
@ -1369,8 +1423,11 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/batch",
|
||||
method: "PATCH",
|
||||
url: "/batch",
|
||||
config: {
|
||||
rateLimit: secretsLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
@ -1492,8 +1549,11 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/batch",
|
||||
method: "DELETE",
|
||||
url: "/batch",
|
||||
config: {
|
||||
rateLimit: secretsLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
workspaceId: z.string().trim(),
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { ApiKeysSchema } from "@app/db/schemas/api-keys";
|
||||
import { readLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -8,6 +9,9 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/me/api-keys",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
|
@ -7,6 +7,7 @@ export enum AuthMethod {
|
||||
AZURE_SAML = "azure-saml",
|
||||
JUMPCLOUD_SAML = "jumpcloud-saml",
|
||||
GOOGLE_SAML = "google-saml",
|
||||
KEYCLOAK_SAML = "keycloak-saml",
|
||||
LDAP = "ldap"
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,11 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
||||
`${TableName.IdentityProjectMembershipRole}.customRoleId`,
|
||||
`${TableName.ProjectRoles}.id`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.IdentityProjectAdditionalPrivilege,
|
||||
`${TableName.IdentityProjectMembership}.id`,
|
||||
`${TableName.IdentityProjectAdditionalPrivilege}.projectMembershipId`
|
||||
)
|
||||
.select(
|
||||
db.ref("id").withSchema(TableName.IdentityProjectMembership),
|
||||
db.ref("createdAt").withSchema(TableName.IdentityProjectMembership),
|
||||
|
@ -14,16 +14,25 @@ import {
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
|
||||
import { ActorAuthMethod, ActorType } from "../auth/auth-type";
|
||||
import { TIdentityProjectMembershipRoleDALFactory } from "../identity-project/identity-project-membership-role-dal";
|
||||
import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal";
|
||||
import { TProjectRoleDALFactory } from "./project-role-dal";
|
||||
|
||||
type TProjectRoleServiceFactoryDep = {
|
||||
projectRoleDAL: TProjectRoleDALFactory;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getUserProjectPermission">;
|
||||
identityProjectMembershipRoleDAL: TIdentityProjectMembershipRoleDALFactory;
|
||||
projectUserMembershipRoleDAL: TProjectUserMembershipRoleDALFactory;
|
||||
};
|
||||
|
||||
export type TProjectRoleServiceFactory = ReturnType<typeof projectRoleServiceFactory>;
|
||||
|
||||
export const projectRoleServiceFactory = ({ projectRoleDAL, permissionService }: TProjectRoleServiceFactoryDep) => {
|
||||
export const projectRoleServiceFactory = ({
|
||||
projectRoleDAL,
|
||||
permissionService,
|
||||
identityProjectMembershipRoleDAL,
|
||||
projectUserMembershipRoleDAL
|
||||
}: TProjectRoleServiceFactoryDep) => {
|
||||
const createRole = async (
|
||||
actor: ActorType,
|
||||
actorId: string,
|
||||
@ -96,8 +105,25 @@ export const projectRoleServiceFactory = ({ projectRoleDAL, permissionService }:
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Role);
|
||||
|
||||
const identityRole = await identityProjectMembershipRoleDAL.findOne({ customRoleId: roleId });
|
||||
const projectUserRole = await projectUserMembershipRoleDAL.findOne({ customRoleId: roleId });
|
||||
|
||||
if (identityRole) {
|
||||
throw new BadRequestError({
|
||||
message: "The role is assigned to one or more identities. Make sure to unassign them before deleting the role.",
|
||||
name: "Delete role"
|
||||
});
|
||||
}
|
||||
if (projectUserRole) {
|
||||
throw new BadRequestError({
|
||||
message: "The role is assigned to one or more users. Make sure to unassign them before deleting the role.",
|
||||
name: "Delete role"
|
||||
});
|
||||
}
|
||||
|
||||
const [deletedRole] = await projectRoleDAL.delete({ id: roleId, projectId });
|
||||
if (!deletedRole) throw new BadRequestError({ message: "Role not found", name: "Update role" });
|
||||
if (!deletedRole) throw new BadRequestError({ message: "Role not found", name: "Delete role" });
|
||||
|
||||
return deletedRole;
|
||||
};
|
||||
|
@ -6,6 +6,7 @@ import { TLicenseServiceFactory } from "@app/ee/services/license/license-service
|
||||
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { createSecretBlindIndex } from "@app/lib/crypto";
|
||||
@ -65,6 +66,7 @@ type TProjectServiceFactoryDep = {
|
||||
orgService: Pick<TOrgServiceFactory, "addGhostUser">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findOne">;
|
||||
keyStore: Pick<TKeyStoreFactory, "deleteItem">;
|
||||
};
|
||||
|
||||
export type TProjectServiceFactory = ReturnType<typeof projectServiceFactory>;
|
||||
@ -86,7 +88,8 @@ export const projectServiceFactory = ({
|
||||
projectEnvDAL,
|
||||
licenseService,
|
||||
projectUserMembershipRoleDAL,
|
||||
identityProjectMembershipRoleDAL
|
||||
identityProjectMembershipRoleDAL,
|
||||
keyStore
|
||||
}: TProjectServiceFactoryDep) => {
|
||||
/*
|
||||
* Create workspace. Make user the admin
|
||||
@ -323,6 +326,7 @@ export const projectServiceFactory = ({
|
||||
};
|
||||
});
|
||||
|
||||
await keyStore.deleteItem(`infisical-cloud-plan-${actorOrgId}`);
|
||||
return results;
|
||||
};
|
||||
|
||||
@ -350,6 +354,7 @@ export const projectServiceFactory = ({
|
||||
return delProject;
|
||||
});
|
||||
|
||||
await keyStore.deleteItem(`infisical-cloud-plan-${actorOrgId}`);
|
||||
return deletedProject;
|
||||
};
|
||||
|
||||
|
@ -70,9 +70,31 @@ export const secretImportDALFactory = (db: TDbClient) => {
|
||||
}
|
||||
};
|
||||
|
||||
const findByFolderIds = async (folderIds: string[], tx?: Knex) => {
|
||||
try {
|
||||
const docs = await (tx || db)(TableName.SecretImport)
|
||||
.whereIn("folderId", folderIds)
|
||||
.join(TableName.Environment, `${TableName.SecretImport}.importEnv`, `${TableName.Environment}.id`)
|
||||
.select(
|
||||
db.ref("*").withSchema(TableName.SecretImport) as unknown as keyof TSecretImports,
|
||||
db.ref("slug").withSchema(TableName.Environment),
|
||||
db.ref("name").withSchema(TableName.Environment),
|
||||
db.ref("id").withSchema(TableName.Environment).as("envId")
|
||||
)
|
||||
.orderBy("position", "asc");
|
||||
return docs.map(({ envId, slug, name, ...el }) => ({
|
||||
...el,
|
||||
importEnv: { id: envId, slug, name }
|
||||
}));
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Find secret imports" });
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
...secretImportOrm,
|
||||
find,
|
||||
findByFolderIds,
|
||||
findLastImportPosition,
|
||||
updateAllPosition
|
||||
};
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user