diff --git a/.github/workflows/release_build_infisical_cli.yml b/.github/workflows/release_build_infisical_cli.yml index 3fe0fbe21..1c16b00b3 100644 --- a/.github/workflows/release_build_infisical_cli.yml +++ b/.github/workflows/release_build_infisical_cli.yml @@ -83,7 +83,7 @@ jobs: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} goreleaser: - runs-on: ubuntu-latest + runs-on: ubuntu-latest-8-cores needs: [cli-integration-tests] steps: - uses: actions/checkout@v3 diff --git a/backend/src/db/instance.ts b/backend/src/db/instance.ts index 3ce8148aa..b1ecf7d65 100644 --- a/backend/src/db/instance.ts +++ b/backend/src/db/instance.ts @@ -110,7 +110,8 @@ export const initAuditLogDbConnection = ({ }, migrations: { tableName: "infisical_migrations" - } + }, + pool: { min: 0, max: 10 } }); // we add these overrides so that auditLogDb and the primary DB are interchangeable diff --git a/backend/src/db/migrations/20250626101747_default-project-type.ts b/backend/src/db/migrations/20250626101747_default-project-type.ts new file mode 100644 index 000000000..5e14b6ea2 --- /dev/null +++ b/backend/src/db/migrations/20250626101747_default-project-type.ts @@ -0,0 +1,41 @@ +import { Knex } from "knex"; + +import { ProjectType, TableName } from "../schemas"; + +export async function up(knex: Knex): Promise { + const hasTypeColumn = await knex.schema.hasColumn(TableName.Project, "type"); + const hasDefaultTypeColumn = await knex.schema.hasColumn(TableName.Project, "defaultProduct"); + if (hasTypeColumn && !hasDefaultTypeColumn) { + await knex.schema.alterTable(TableName.Project, (t) => { + t.string("type").nullable().alter(); + t.string("defaultProduct").notNullable().defaultTo(ProjectType.SecretManager); + }); + + await knex(TableName.Project).update({ + // eslint-disable-next-line + // @ts-ignore this is because this field is created later + defaultProduct: knex.raw(` + CASE + WHEN "type" IS NULL OR "type" = '' THEN 'secret-manager' + ELSE "type" + END + `) + }); + } + + const hasTemplateTypeColumn = await knex.schema.hasColumn(TableName.ProjectTemplates, "type"); + if (hasTemplateTypeColumn) { + await knex.schema.alterTable(TableName.ProjectTemplates, (t) => { + t.string("type").nullable().alter(); + }); + } +} + +export async function down(knex: Knex): Promise { + const hasDefaultTypeColumn = await knex.schema.hasColumn(TableName.Project, "defaultProduct"); + if (hasDefaultTypeColumn) { + await knex.schema.alterTable(TableName.Project, (t) => { + t.dropColumn("defaultProduct"); + }); + } +} diff --git a/backend/src/db/migrations/20250627010508_env-overrides.ts b/backend/src/db/migrations/20250627010508_env-overrides.ts new file mode 100644 index 000000000..535360a80 --- /dev/null +++ b/backend/src/db/migrations/20250627010508_env-overrides.ts @@ -0,0 +1,21 @@ +import { Knex } from "knex"; + +import { TableName } from "../schemas"; + +export async function up(knex: Knex): Promise { + const hasColumn = await knex.schema.hasColumn(TableName.SuperAdmin, "encryptedEnvOverrides"); + if (!hasColumn) { + await knex.schema.alterTable(TableName.SuperAdmin, (t) => { + t.binary("encryptedEnvOverrides").nullable(); + }); + } +} + +export async function down(knex: Knex): Promise { + const hasColumn = await knex.schema.hasColumn(TableName.SuperAdmin, "encryptedEnvOverrides"); + if (hasColumn) { + await knex.schema.alterTable(TableName.SuperAdmin, (t) => { + t.dropColumn("encryptedEnvOverrides"); + }); + } +} diff --git a/backend/src/db/migrations/20250630212553_user-latest-invite.ts b/backend/src/db/migrations/20250630212553_user-latest-invite.ts new file mode 100644 index 000000000..6d8c01248 --- /dev/null +++ b/backend/src/db/migrations/20250630212553_user-latest-invite.ts @@ -0,0 +1,21 @@ +import { Knex } from "knex"; + +import { TableName } from "../schemas"; + +export async function up(knex: Knex): Promise { + const hasColumn = await knex.schema.hasColumn(TableName.OrgMembership, "lastInvitedAt"); + await knex.schema.alterTable(TableName.OrgMembership, (t) => { + if (!hasColumn) { + t.datetime("lastInvitedAt").nullable(); + } + }); +} + +export async function down(knex: Knex): Promise { + const hasColumn = await knex.schema.hasColumn(TableName.OrgMembership, "lastInvitedAt"); + await knex.schema.alterTable(TableName.OrgMembership, (t) => { + if (hasColumn) { + t.dropColumn("lastInvitedAt"); + } + }); +} diff --git a/backend/src/db/schemas/models.ts b/backend/src/db/schemas/models.ts index d47962110..75d36833b 100644 --- a/backend/src/db/schemas/models.ts +++ b/backend/src/db/schemas/models.ts @@ -267,16 +267,6 @@ export enum ProjectType { SecretScanning = "secret-scanning" } -export enum ActionProjectType { - SecretManager = ProjectType.SecretManager, - CertificateManager = ProjectType.CertificateManager, - KMS = ProjectType.KMS, - SSH = ProjectType.SSH, - SecretScanning = ProjectType.SecretScanning, - // project operations that happen on all types - Any = "any" -} - export enum SortDirection { ASC = "asc", DESC = "desc" diff --git a/backend/src/db/schemas/org-memberships.ts b/backend/src/db/schemas/org-memberships.ts index e77b6e9c9..939033c71 100644 --- a/backend/src/db/schemas/org-memberships.ts +++ b/backend/src/db/schemas/org-memberships.ts @@ -18,7 +18,8 @@ export const OrgMembershipsSchema = z.object({ orgId: z.string().uuid(), roleId: z.string().uuid().nullable().optional(), projectFavorites: z.string().array().nullable().optional(), - isActive: z.boolean().default(true) + isActive: z.boolean().default(true), + lastInvitedAt: z.date().nullable().optional() }); export type TOrgMemberships = z.infer; diff --git a/backend/src/db/schemas/project-templates.ts b/backend/src/db/schemas/project-templates.ts index f12386165..d1fe29a80 100644 --- a/backend/src/db/schemas/project-templates.ts +++ b/backend/src/db/schemas/project-templates.ts @@ -16,7 +16,7 @@ export const ProjectTemplatesSchema = z.object({ orgId: z.string().uuid(), createdAt: z.date(), updatedAt: z.date(), - type: z.string().default("secret-manager") + type: z.string().nullable().optional() }); export type TProjectTemplates = z.infer; diff --git a/backend/src/db/schemas/projects.ts b/backend/src/db/schemas/projects.ts index b4c98d8a2..00401575e 100644 --- a/backend/src/db/schemas/projects.ts +++ b/backend/src/db/schemas/projects.ts @@ -25,11 +25,12 @@ export const ProjectsSchema = z.object({ kmsSecretManagerKeyId: z.string().uuid().nullable().optional(), kmsSecretManagerEncryptedDataKey: zodBuffer.nullable().optional(), description: z.string().nullable().optional(), - type: z.string(), + type: z.string().nullable().optional(), enforceCapitalization: z.boolean().default(false), hasDeleteProtection: z.boolean().default(false).nullable().optional(), secretSharing: z.boolean().default(true), - showSnapshotsLegacy: z.boolean().default(false) + showSnapshotsLegacy: z.boolean().default(false), + defaultProduct: z.string().default("secret-manager") }); export type TProjects = z.infer; diff --git a/backend/src/ee/routes/v1/access-approval-request-router.ts b/backend/src/ee/routes/v1/access-approval-request-router.ts index 8a6b2be88..7a70d6374 100644 --- a/backend/src/ee/routes/v1/access-approval-request-router.ts +++ b/backend/src/ee/routes/v1/access-approval-request-router.ts @@ -60,7 +60,8 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv method: "GET", schema: { querystring: z.object({ - projectSlug: z.string().trim() + projectSlug: z.string().trim(), + policyId: z.string().trim().optional() }), response: { 200: z.object({ @@ -73,6 +74,7 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv handler: async (req) => { const { count } = await server.services.accessApprovalRequest.getCount({ projectSlug: req.query.projectSlug, + policyId: req.query.policyId, actor: req.permission.type, actorId: req.permission.id, actorOrgId: req.permission.orgId, diff --git a/backend/src/ee/routes/v1/ldap-router.ts b/backend/src/ee/routes/v1/ldap-router.ts index 57c5736df..7c520e2ab 100644 --- a/backend/src/ee/routes/v1/ldap-router.ts +++ b/backend/src/ee/routes/v1/ldap-router.ts @@ -17,6 +17,7 @@ import { z } from "zod"; import { LdapGroupMapsSchema } from "@app/db/schemas"; import { TLDAPConfig } from "@app/ee/services/ldap-config/ldap-config-types"; import { isValidLdapFilter, searchGroups } from "@app/ee/services/ldap-config/ldap-fns"; +import { ApiDocsTags, LdapSso } from "@app/lib/api-docs"; import { getConfig } from "@app/lib/config/env"; import { BadRequestError } from "@app/lib/errors"; import { logger } from "@app/lib/logger"; @@ -132,10 +133,18 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => { config: { rateLimit: readLimit }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), schema: { + hide: false, + tags: [ApiDocsTags.LdapSso], + description: "Get LDAP config", + security: [ + { + bearerAuth: [] + } + ], querystring: z.object({ - organizationId: z.string().trim() + organizationId: z.string().trim().describe(LdapSso.GET_CONFIG.organizationId) }), response: { 200: z.object({ @@ -172,23 +181,32 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => { config: { rateLimit: writeLimit }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), schema: { + hide: false, + tags: [ApiDocsTags.LdapSso], + description: "Create LDAP config", + security: [ + { + bearerAuth: [] + } + ], body: z.object({ - organizationId: z.string().trim(), - isActive: z.boolean(), - url: z.string().trim(), - bindDN: z.string().trim(), - bindPass: z.string().trim(), - uniqueUserAttribute: z.string().trim().default("uidNumber"), - searchBase: z.string().trim(), - searchFilter: z.string().trim().default("(uid={{username}})"), - groupSearchBase: z.string().trim(), + organizationId: z.string().trim().describe(LdapSso.CREATE_CONFIG.organizationId), + isActive: z.boolean().describe(LdapSso.CREATE_CONFIG.isActive), + url: z.string().trim().describe(LdapSso.CREATE_CONFIG.url), + bindDN: z.string().trim().describe(LdapSso.CREATE_CONFIG.bindDN), + bindPass: z.string().trim().describe(LdapSso.CREATE_CONFIG.bindPass), + uniqueUserAttribute: z.string().trim().default("uidNumber").describe(LdapSso.CREATE_CONFIG.uniqueUserAttribute), + searchBase: z.string().trim().describe(LdapSso.CREATE_CONFIG.searchBase), + searchFilter: z.string().trim().default("(uid={{username}})").describe(LdapSso.CREATE_CONFIG.searchFilter), + groupSearchBase: z.string().trim().describe(LdapSso.CREATE_CONFIG.groupSearchBase), groupSearchFilter: z .string() .trim() - .default("(|(memberUid={{.Username}})(member={{.UserDN}})(uniqueMember={{.UserDN}}))"), - caCert: z.string().trim().default("") + .default("(|(memberUid={{.Username}})(member={{.UserDN}})(uniqueMember={{.UserDN}}))") + .describe(LdapSso.CREATE_CONFIG.groupSearchFilter), + caCert: z.string().trim().default("").describe(LdapSso.CREATE_CONFIG.caCert) }), response: { 200: SanitizedLdapConfigSchema @@ -214,23 +232,31 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => { config: { rateLimit: writeLimit }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), schema: { + hide: false, + tags: [ApiDocsTags.LdapSso], + description: "Update LDAP config", + security: [ + { + bearerAuth: [] + } + ], body: z .object({ - isActive: z.boolean(), - url: z.string().trim(), - bindDN: z.string().trim(), - bindPass: z.string().trim(), - uniqueUserAttribute: z.string().trim(), - searchBase: z.string().trim(), - searchFilter: z.string().trim(), - groupSearchBase: z.string().trim(), - groupSearchFilter: z.string().trim(), - caCert: z.string().trim() + isActive: z.boolean().describe(LdapSso.UPDATE_CONFIG.isActive), + url: z.string().trim().describe(LdapSso.UPDATE_CONFIG.url), + bindDN: z.string().trim().describe(LdapSso.UPDATE_CONFIG.bindDN), + bindPass: z.string().trim().describe(LdapSso.UPDATE_CONFIG.bindPass), + uniqueUserAttribute: z.string().trim().describe(LdapSso.UPDATE_CONFIG.uniqueUserAttribute), + searchBase: z.string().trim().describe(LdapSso.UPDATE_CONFIG.searchBase), + searchFilter: z.string().trim().describe(LdapSso.UPDATE_CONFIG.searchFilter), + groupSearchBase: z.string().trim().describe(LdapSso.UPDATE_CONFIG.groupSearchBase), + groupSearchFilter: z.string().trim().describe(LdapSso.UPDATE_CONFIG.groupSearchFilter), + caCert: z.string().trim().describe(LdapSso.UPDATE_CONFIG.caCert) }) .partial() - .merge(z.object({ organizationId: z.string() })), + .merge(z.object({ organizationId: z.string().trim().describe(LdapSso.UPDATE_CONFIG.organizationId) })), response: { 200: SanitizedLdapConfigSchema } diff --git a/backend/src/ee/routes/v1/oidc-router.ts b/backend/src/ee/routes/v1/oidc-router.ts index 1bfc4d696..59c05272d 100644 --- a/backend/src/ee/routes/v1/oidc-router.ts +++ b/backend/src/ee/routes/v1/oidc-router.ts @@ -13,6 +13,7 @@ import { z } from "zod"; import { OidcConfigsSchema } from "@app/db/schemas"; import { OIDCConfigurationType, OIDCJWTSignatureAlgorithm } from "@app/ee/services/oidc/oidc-config-types"; +import { ApiDocsTags, OidcSSo } from "@app/lib/api-docs"; import { getConfig } from "@app/lib/config/env"; import { authRateLimit, readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; @@ -153,10 +154,18 @@ export const registerOidcRouter = async (server: FastifyZodProvider) => { config: { rateLimit: readLimit }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), schema: { + hide: false, + tags: [ApiDocsTags.OidcSso], + description: "Get OIDC config", + security: [ + { + bearerAuth: [] + } + ], querystring: z.object({ - orgSlug: z.string().trim() + organizationId: z.string().trim().describe(OidcSSo.GET_CONFIG.organizationId) }), response: { 200: SanitizedOidcConfigSchema.pick({ @@ -180,9 +189,8 @@ export const registerOidcRouter = async (server: FastifyZodProvider) => { } }, handler: async (req) => { - const { orgSlug } = req.query; const oidc = await server.services.oidc.getOidc({ - orgSlug, + organizationId: req.query.organizationId, type: "external", actor: req.permission.type, actorId: req.permission.id, @@ -200,8 +208,16 @@ export const registerOidcRouter = async (server: FastifyZodProvider) => { config: { rateLimit: writeLimit }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), schema: { + hide: false, + tags: [ApiDocsTags.OidcSso], + description: "Update OIDC config", + security: [ + { + bearerAuth: [] + } + ], body: z .object({ allowedEmailDomains: z @@ -216,22 +232,26 @@ export const registerOidcRouter = async (server: FastifyZodProvider) => { .split(",") .map((id) => id.trim()) .join(", "); - }), - discoveryURL: z.string().trim(), - configurationType: z.nativeEnum(OIDCConfigurationType), - issuer: z.string().trim(), - authorizationEndpoint: z.string().trim(), - jwksUri: z.string().trim(), - tokenEndpoint: z.string().trim(), - userinfoEndpoint: z.string().trim(), - clientId: z.string().trim(), - clientSecret: z.string().trim(), - isActive: z.boolean(), - manageGroupMemberships: z.boolean().optional(), - jwtSignatureAlgorithm: z.nativeEnum(OIDCJWTSignatureAlgorithm).optional() + }) + .describe(OidcSSo.UPDATE_CONFIG.allowedEmailDomains), + discoveryURL: z.string().trim().describe(OidcSSo.UPDATE_CONFIG.discoveryURL), + configurationType: z.nativeEnum(OIDCConfigurationType).describe(OidcSSo.UPDATE_CONFIG.configurationType), + issuer: z.string().trim().describe(OidcSSo.UPDATE_CONFIG.issuer), + authorizationEndpoint: z.string().trim().describe(OidcSSo.UPDATE_CONFIG.authorizationEndpoint), + jwksUri: z.string().trim().describe(OidcSSo.UPDATE_CONFIG.jwksUri), + tokenEndpoint: z.string().trim().describe(OidcSSo.UPDATE_CONFIG.tokenEndpoint), + userinfoEndpoint: z.string().trim().describe(OidcSSo.UPDATE_CONFIG.userinfoEndpoint), + clientId: z.string().trim().describe(OidcSSo.UPDATE_CONFIG.clientId), + clientSecret: z.string().trim().describe(OidcSSo.UPDATE_CONFIG.clientSecret), + isActive: z.boolean().describe(OidcSSo.UPDATE_CONFIG.isActive), + manageGroupMemberships: z.boolean().optional().describe(OidcSSo.UPDATE_CONFIG.manageGroupMemberships), + jwtSignatureAlgorithm: z + .nativeEnum(OIDCJWTSignatureAlgorithm) + .optional() + .describe(OidcSSo.UPDATE_CONFIG.jwtSignatureAlgorithm) }) .partial() - .merge(z.object({ orgSlug: z.string() })), + .merge(z.object({ organizationId: z.string().describe(OidcSSo.UPDATE_CONFIG.organizationId) })), response: { 200: SanitizedOidcConfigSchema.pick({ id: true, @@ -267,8 +287,16 @@ export const registerOidcRouter = async (server: FastifyZodProvider) => { config: { rateLimit: writeLimit }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), schema: { + hide: false, + tags: [ApiDocsTags.OidcSso], + description: "Create OIDC config", + security: [ + { + bearerAuth: [] + } + ], body: z .object({ allowedEmailDomains: z @@ -283,23 +311,34 @@ export const registerOidcRouter = async (server: FastifyZodProvider) => { .split(",") .map((id) => id.trim()) .join(", "); - }), - configurationType: z.nativeEnum(OIDCConfigurationType), - issuer: z.string().trim().optional().default(""), - discoveryURL: z.string().trim().optional().default(""), - authorizationEndpoint: z.string().trim().optional().default(""), - jwksUri: z.string().trim().optional().default(""), - tokenEndpoint: z.string().trim().optional().default(""), - userinfoEndpoint: z.string().trim().optional().default(""), - clientId: z.string().trim(), - clientSecret: z.string().trim(), - isActive: z.boolean(), - orgSlug: z.string().trim(), - manageGroupMemberships: z.boolean().optional().default(false), + }) + .describe(OidcSSo.CREATE_CONFIG.allowedEmailDomains), + configurationType: z.nativeEnum(OIDCConfigurationType).describe(OidcSSo.CREATE_CONFIG.configurationType), + issuer: z.string().trim().optional().default("").describe(OidcSSo.CREATE_CONFIG.issuer), + discoveryURL: z.string().trim().optional().default("").describe(OidcSSo.CREATE_CONFIG.discoveryURL), + authorizationEndpoint: z + .string() + .trim() + .optional() + .default("") + .describe(OidcSSo.CREATE_CONFIG.authorizationEndpoint), + jwksUri: z.string().trim().optional().default("").describe(OidcSSo.CREATE_CONFIG.jwksUri), + tokenEndpoint: z.string().trim().optional().default("").describe(OidcSSo.CREATE_CONFIG.tokenEndpoint), + userinfoEndpoint: z.string().trim().optional().default("").describe(OidcSSo.CREATE_CONFIG.userinfoEndpoint), + clientId: z.string().trim().describe(OidcSSo.CREATE_CONFIG.clientId), + clientSecret: z.string().trim().describe(OidcSSo.CREATE_CONFIG.clientSecret), + isActive: z.boolean().describe(OidcSSo.CREATE_CONFIG.isActive), + organizationId: z.string().trim().describe(OidcSSo.CREATE_CONFIG.organizationId), + manageGroupMemberships: z + .boolean() + .optional() + .default(false) + .describe(OidcSSo.CREATE_CONFIG.manageGroupMemberships), jwtSignatureAlgorithm: z .nativeEnum(OIDCJWTSignatureAlgorithm) .optional() .default(OIDCJWTSignatureAlgorithm.RS256) + .describe(OidcSSo.CREATE_CONFIG.jwtSignatureAlgorithm) }) .superRefine((data, ctx) => { if (data.configurationType === OIDCConfigurationType.CUSTOM) { diff --git a/backend/src/ee/routes/v1/project-router.ts b/backend/src/ee/routes/v1/project-router.ts index ab9d1be6c..8d8cc4817 100644 --- a/backend/src/ee/routes/v1/project-router.ts +++ b/backend/src/ee/routes/v1/project-router.ts @@ -111,15 +111,38 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { params: z.object({ workspaceId: z.string().trim().describe(AUDIT_LOGS.EXPORT.projectId) }), - querystring: z.object({ - eventType: z.nativeEnum(EventType).optional().describe(AUDIT_LOGS.EXPORT.eventType), - userAgentType: z.nativeEnum(UserAgentType).optional().describe(AUDIT_LOGS.EXPORT.userAgentType), - startDate: z.string().datetime().optional().describe(AUDIT_LOGS.EXPORT.startDate), - endDate: z.string().datetime().optional().describe(AUDIT_LOGS.EXPORT.endDate), - offset: z.coerce.number().default(0).describe(AUDIT_LOGS.EXPORT.offset), - limit: z.coerce.number().default(20).describe(AUDIT_LOGS.EXPORT.limit), - actor: z.string().optional().describe(AUDIT_LOGS.EXPORT.actor) - }), + querystring: z + .object({ + eventType: z.nativeEnum(EventType).optional().describe(AUDIT_LOGS.EXPORT.eventType), + userAgentType: z.nativeEnum(UserAgentType).optional().describe(AUDIT_LOGS.EXPORT.userAgentType), + startDate: z.string().datetime().optional().describe(AUDIT_LOGS.EXPORT.startDate), + endDate: z.string().datetime().optional().describe(AUDIT_LOGS.EXPORT.endDate), + offset: z.coerce.number().default(0).describe(AUDIT_LOGS.EXPORT.offset), + limit: z.coerce.number().max(1000).default(20).describe(AUDIT_LOGS.EXPORT.limit), + actor: z.string().optional().describe(AUDIT_LOGS.EXPORT.actor) + }) + .superRefine((el, ctx) => { + if (el.endDate && el.startDate) { + const startDate = new Date(el.startDate); + const endDate = new Date(el.endDate); + const maxAllowedDate = new Date(startDate); + maxAllowedDate.setMonth(maxAllowedDate.getMonth() + 3); + if (endDate < startDate) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: ["endDate"], + message: "End date cannot be before start date" + }); + } + if (endDate > maxAllowedDate) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: ["endDate"], + message: "Dates must be within 3 months" + }); + } + } + }), response: { 200: z.object({ auditLogs: AuditLogsSchema.omit({ @@ -161,7 +184,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { filter: { ...req.query, projectId: req.params.workspaceId, - endDate: req.query.endDate, + endDate: req.query.endDate || new Date().toISOString(), startDate: req.query.startDate || getLastMidnightDateISO(), auditLogActorId: req.query.actor, eventType: req.query.eventType ? [req.query.eventType] : undefined diff --git a/backend/src/ee/routes/v1/project-template-router.ts b/backend/src/ee/routes/v1/project-template-router.ts index 5d33b4d58..a00f4aa0b 100644 --- a/backend/src/ee/routes/v1/project-template-router.ts +++ b/backend/src/ee/routes/v1/project-template-router.ts @@ -1,6 +1,6 @@ import { z } from "zod"; -import { ProjectMembershipRole, ProjectTemplatesSchema, ProjectType } from "@app/db/schemas"; +import { ProjectMembershipRole, ProjectTemplatesSchema } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission"; import { isInfisicalProjectTemplate } from "@app/ee/services/project-template/project-template-fns"; @@ -104,9 +104,6 @@ export const registerProjectTemplateRouter = async (server: FastifyZodProvider) hide: false, tags: [ApiDocsTags.ProjectTemplates], description: "List project templates for the current organization.", - querystring: z.object({ - type: z.nativeEnum(ProjectType).optional().describe(ProjectTemplates.LIST.type) - }), response: { 200: z.object({ projectTemplates: SanitizedProjectTemplateSchema.array() @@ -115,8 +112,7 @@ export const registerProjectTemplateRouter = async (server: FastifyZodProvider) }, onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { - const { type } = req.query; - const projectTemplates = await server.services.projectTemplate.listProjectTemplatesByOrg(req.permission, type); + const projectTemplates = await server.services.projectTemplate.listProjectTemplatesByOrg(req.permission); const auditTemplates = projectTemplates.filter((template) => !isInfisicalProjectTemplate(template.name)); @@ -188,7 +184,6 @@ export const registerProjectTemplateRouter = async (server: FastifyZodProvider) tags: [ApiDocsTags.ProjectTemplates], description: "Create a project template.", body: z.object({ - type: z.nativeEnum(ProjectType).describe(ProjectTemplates.CREATE.type), name: slugSchema({ field: "name" }) .refine((val) => !isInfisicalProjectTemplate(val), { message: `The requested project template name is reserved.` @@ -284,7 +279,6 @@ export const registerProjectTemplateRouter = async (server: FastifyZodProvider) tags: [ApiDocsTags.ProjectTemplates], description: "Delete a project template.", params: z.object({ templateId: z.string().uuid().describe(ProjectTemplates.DELETE.templateId) }), - response: { 200: z.object({ projectTemplate: SanitizedProjectTemplateSchema diff --git a/backend/src/ee/routes/v1/saml-router.ts b/backend/src/ee/routes/v1/saml-router.ts index c8395d608..f8e371d01 100644 --- a/backend/src/ee/routes/v1/saml-router.ts +++ b/backend/src/ee/routes/v1/saml-router.ts @@ -13,6 +13,7 @@ import { FastifyRequest } from "fastify"; import { z } from "zod"; import { SamlProviders, TGetSamlCfgDTO } from "@app/ee/services/saml-config/saml-config-types"; +import { ApiDocsTags, SamlSso } from "@app/lib/api-docs"; import { getConfig } from "@app/lib/config/env"; import { BadRequestError } from "@app/lib/errors"; import { logger } from "@app/lib/logger"; @@ -149,8 +150,8 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => { firstName, lastName: lastName as string, relayState: (req.body as { RelayState?: string }).RelayState, - authProvider: (req as unknown as FastifyRequest).ssoConfig?.authProvider as string, - orgId: (req as unknown as FastifyRequest).ssoConfig?.orgId as string, + authProvider: (req as unknown as FastifyRequest).ssoConfig?.authProvider, + orgId: (req as unknown as FastifyRequest).ssoConfig?.orgId, metadata: userMetadata }); cb(null, { isUserCompleted, providerAuthToken }); @@ -262,25 +263,31 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => { config: { rateLimit: readLimit }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), schema: { + hide: false, + tags: [ApiDocsTags.SamlSso], + description: "Get SAML config", + security: [ + { + bearerAuth: [] + } + ], querystring: z.object({ - organizationId: z.string().trim() + organizationId: z.string().trim().describe(SamlSso.GET_CONFIG.organizationId) }), response: { - 200: z - .object({ - id: z.string(), - organization: z.string(), - orgId: z.string(), - authProvider: z.string(), - isActive: z.boolean(), - entryPoint: z.string(), - issuer: z.string(), - cert: z.string(), - lastUsed: z.date().nullable().optional() - }) - .optional() + 200: z.object({ + id: z.string(), + organization: z.string(), + orgId: z.string(), + authProvider: z.string(), + isActive: z.boolean(), + entryPoint: z.string(), + issuer: z.string(), + cert: z.string(), + lastUsed: z.date().nullable().optional() + }) } }, handler: async (req) => { @@ -302,15 +309,23 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => { config: { rateLimit: writeLimit }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), schema: { + hide: false, + tags: [ApiDocsTags.SamlSso], + description: "Create SAML config", + security: [ + { + bearerAuth: [] + } + ], body: z.object({ - organizationId: z.string(), - authProvider: z.nativeEnum(SamlProviders), - isActive: z.boolean(), - entryPoint: z.string(), - issuer: z.string(), - cert: z.string() + organizationId: z.string().trim().describe(SamlSso.CREATE_CONFIG.organizationId), + authProvider: z.nativeEnum(SamlProviders).describe(SamlSso.CREATE_CONFIG.authProvider), + isActive: z.boolean().describe(SamlSso.CREATE_CONFIG.isActive), + entryPoint: z.string().trim().describe(SamlSso.CREATE_CONFIG.entryPoint), + issuer: z.string().trim().describe(SamlSso.CREATE_CONFIG.issuer), + cert: z.string().trim().describe(SamlSso.CREATE_CONFIG.cert) }), response: { 200: SanitizedSamlConfigSchema @@ -341,18 +356,26 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => { config: { rateLimit: writeLimit }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), schema: { + hide: false, + tags: [ApiDocsTags.SamlSso], + description: "Update SAML config", + security: [ + { + bearerAuth: [] + } + ], body: z .object({ - authProvider: z.nativeEnum(SamlProviders), - isActive: z.boolean(), - entryPoint: z.string(), - issuer: z.string(), - cert: z.string() + authProvider: z.nativeEnum(SamlProviders).describe(SamlSso.UPDATE_CONFIG.authProvider), + isActive: z.boolean().describe(SamlSso.UPDATE_CONFIG.isActive), + entryPoint: z.string().trim().describe(SamlSso.UPDATE_CONFIG.entryPoint), + issuer: z.string().trim().describe(SamlSso.UPDATE_CONFIG.issuer), + cert: z.string().trim().describe(SamlSso.UPDATE_CONFIG.cert) }) .partial() - .merge(z.object({ organizationId: z.string() })), + .merge(z.object({ organizationId: z.string().trim().describe(SamlSso.UPDATE_CONFIG.organizationId) })), response: { 200: SanitizedSamlConfigSchema } diff --git a/backend/src/ee/routes/v1/secret-approval-request-router.ts b/backend/src/ee/routes/v1/secret-approval-request-router.ts index e558062b1..d53124e52 100644 --- a/backend/src/ee/routes/v1/secret-approval-request-router.ts +++ b/backend/src/ee/routes/v1/secret-approval-request-router.ts @@ -94,7 +94,8 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv }, schema: { querystring: z.object({ - workspaceId: z.string().trim() + workspaceId: z.string().trim(), + policyId: z.string().trim().optional() }), response: { 200: z.object({ @@ -112,7 +113,8 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv actorId: req.permission.id, actorAuthMethod: req.permission.authMethod, actorOrgId: req.permission.orgId, - projectId: req.query.workspaceId + projectId: req.query.workspaceId, + policyId: req.query.policyId }); return { approvals }; } diff --git a/backend/src/ee/services/access-approval-policy/access-approval-policy-service.ts b/backend/src/ee/services/access-approval-policy/access-approval-policy-service.ts index 4e818df2a..5976f5fff 100644 --- a/backend/src/ee/services/access-approval-policy/access-approval-policy-service.ts +++ b/backend/src/ee/services/access-approval-policy/access-approval-policy-service.ts @@ -1,6 +1,5 @@ import { ForbiddenError } from "@casl/ability"; -import { ActionProjectType } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors"; @@ -97,8 +96,7 @@ export const accessApprovalPolicyServiceFactory = ({ actorId, projectId: project.id, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -248,8 +246,7 @@ export const accessApprovalPolicyServiceFactory = ({ actorId, projectId: project.id, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const accessApprovalPolicies = await accessApprovalPolicyDAL.find({ projectId: project.id, deletedAt: null }); @@ -301,8 +298,7 @@ export const accessApprovalPolicyServiceFactory = ({ actorId, projectId: accessApprovalPolicy.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval); @@ -498,8 +494,7 @@ export const accessApprovalPolicyServiceFactory = ({ actorId, projectId: policy.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, @@ -549,8 +544,7 @@ export const accessApprovalPolicyServiceFactory = ({ actorId, projectId: project.id, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); if (!membership) { throw new ForbiddenRequestError({ message: "You are not a member of this project" }); @@ -589,8 +583,7 @@ export const accessApprovalPolicyServiceFactory = ({ actorId, projectId: policy.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval); diff --git a/backend/src/ee/services/access-approval-request/access-approval-request-dal.ts b/backend/src/ee/services/access-approval-request/access-approval-request-dal.ts index 33e9f7a32..671d2c1de 100644 --- a/backend/src/ee/services/access-approval-request/access-approval-request-dal.ts +++ b/backend/src/ee/services/access-approval-request/access-approval-request-dal.ts @@ -220,7 +220,7 @@ export interface TAccessApprovalRequestDALFactory extends Omit; - getCount: ({ projectId }: { projectId: string }) => Promise<{ + getCount: ({ projectId }: { projectId: string; policyId?: string }) => Promise<{ pendingCount: number; finalizedCount: number; }>; @@ -702,7 +702,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient): TAccessApprovalR } }; - const getCount: TAccessApprovalRequestDALFactory["getCount"] = async ({ projectId }) => { + const getCount: TAccessApprovalRequestDALFactory["getCount"] = async ({ projectId, policyId }) => { try { const accessRequests = await db .replicaNode()(TableName.AccessApprovalRequest) @@ -723,8 +723,10 @@ export const accessApprovalRequestDALFactory = (db: TDbClient): TAccessApprovalR `${TableName.AccessApprovalRequest}.id`, `${TableName.AccessApprovalRequestReviewer}.requestId` ) - .where(`${TableName.Environment}.projectId`, projectId) + .where((qb) => { + if (policyId) void qb.where(`${TableName.AccessApprovalPolicy}.id`, policyId); + }) .select(selectAllTableCols(TableName.AccessApprovalRequest)) .select(db.ref("status").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerStatus")) .select(db.ref("reviewerUserId").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerUserId")) diff --git a/backend/src/ee/services/access-approval-request/access-approval-request-service.ts b/backend/src/ee/services/access-approval-request/access-approval-request-service.ts index ccab70d5b..4cee898f1 100644 --- a/backend/src/ee/services/access-approval-request/access-approval-request-service.ts +++ b/backend/src/ee/services/access-approval-request/access-approval-request-service.ts @@ -1,7 +1,7 @@ import slugify from "@sindresorhus/slugify"; import msFn from "ms"; -import { ActionProjectType, ProjectMembershipRole } from "@app/db/schemas"; +import { ProjectMembershipRole } from "@app/db/schemas"; import { getConfig } from "@app/lib/config/env"; import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors"; import { groupBy } from "@app/lib/fn"; @@ -107,8 +107,7 @@ export const accessApprovalRequestServiceFactory = ({ actorId, projectId: project.id, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); if (!membership) { throw new ForbiddenRequestError({ message: "You are not a member of this project" }); @@ -217,7 +216,7 @@ export const accessApprovalRequestServiceFactory = ({ ); const requesterFullName = `${requestedByUser.firstName} ${requestedByUser.lastName}`; - const approvalUrl = `${cfg.SITE_URL}/secret-manager/${project.id}/approval`; + const approvalUrl = `${cfg.SITE_URL}/projects/${project.id}/secret-manager/approval`; await triggerWorkflowIntegrationNotification({ input: { @@ -290,8 +289,7 @@ export const accessApprovalRequestServiceFactory = ({ actorId, projectId: project.id, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); if (!membership) { throw new ForbiddenRequestError({ message: "You are not a member of this project" }); @@ -337,8 +335,7 @@ export const accessApprovalRequestServiceFactory = ({ actorId, projectId: accessApprovalRequest.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); if (!membership) { @@ -548,7 +545,7 @@ export const accessApprovalRequestServiceFactory = ({ bypassReason: bypassReason || "No reason provided", secretPath: policy.secretPath || "/", environment, - approvalUrl: `${cfg.SITE_URL}/secret-manager/${project.id}/approval`, + approvalUrl: `${cfg.SITE_URL}/projects/${project.id}/secret-manager/approval`, requestType: "access" }, template: SmtpTemplates.AccessSecretRequestBypassed @@ -565,6 +562,7 @@ export const accessApprovalRequestServiceFactory = ({ const getCount: TAccessApprovalRequestServiceFactory["getCount"] = async ({ projectSlug, + policyId, actor, actorAuthMethod, actorId, @@ -578,14 +576,13 @@ export const accessApprovalRequestServiceFactory = ({ actorId, projectId: project.id, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); if (!membership) { throw new ForbiddenRequestError({ message: "You are not a member of this project" }); } - const count = await accessApprovalRequestDAL.getCount({ projectId: project.id }); + const count = await accessApprovalRequestDAL.getCount({ projectId: project.id, policyId }); return { count }; }; diff --git a/backend/src/ee/services/access-approval-request/access-approval-request-types.ts b/backend/src/ee/services/access-approval-request/access-approval-request-types.ts index 2550f2a96..9066aec8f 100644 --- a/backend/src/ee/services/access-approval-request/access-approval-request-types.ts +++ b/backend/src/ee/services/access-approval-request/access-approval-request-types.ts @@ -12,6 +12,7 @@ export type TVerifyPermission = { export type TGetAccessRequestCountDTO = { projectSlug: string; + policyId?: string; } & Omit; export type TReviewAccessRequestDTO = { diff --git a/backend/src/ee/services/assume-privilege/assume-privilege-service.ts b/backend/src/ee/services/assume-privilege/assume-privilege-service.ts index d4a643d8b..c1cfad082 100644 --- a/backend/src/ee/services/assume-privilege/assume-privilege-service.ts +++ b/backend/src/ee/services/assume-privilege/assume-privilege-service.ts @@ -1,7 +1,6 @@ import { ForbiddenError } from "@casl/ability"; import jwt from "jsonwebtoken"; -import { ActionProjectType } from "@app/db/schemas"; import { getConfig } from "@app/lib/config/env"; import { ForbiddenRequestError, NotFoundError } from "@app/lib/errors"; import { ActorType } from "@app/services/auth/auth-type"; @@ -38,8 +37,7 @@ export const assumePrivilegeServiceFactory = ({ actorId: actorPermissionDetails.id, projectId, actorAuthMethod: actorPermissionDetails.authMethod, - actorOrgId: actorPermissionDetails.orgId, - actionProjectType: ActionProjectType.Any + actorOrgId: actorPermissionDetails.orgId }); if (targetActorType === ActorType.USER) { @@ -60,8 +58,7 @@ export const assumePrivilegeServiceFactory = ({ actorId: targetActorId, projectId, actorAuthMethod: actorPermissionDetails.authMethod, - actorOrgId: actorPermissionDetails.orgId, - actionProjectType: ActionProjectType.Any + actorOrgId: actorPermissionDetails.orgId }); const appCfg = getConfig(); diff --git a/backend/src/ee/services/audit-log/audit-log-dal.ts b/backend/src/ee/services/audit-log/audit-log-dal.ts index 874460b36..2df779795 100644 --- a/backend/src/ee/services/audit-log/audit-log-dal.ts +++ b/backend/src/ee/services/audit-log/audit-log-dal.ts @@ -30,10 +30,10 @@ type TFindQuery = { actor?: string; projectId?: string; environment?: string; - orgId?: string; + orgId: string; eventType?: string; - startDate?: string; - endDate?: string; + startDate: string; + endDate: string; userAgentType?: string; limit?: number; offset?: number; @@ -61,18 +61,15 @@ export const auditLogDALFactory = (db: TDbClient) => { }, tx ) => { - if (!orgId && !projectId) { - throw new Error("Either orgId or projectId must be provided"); - } - try { // Find statements const sqlQuery = (tx || db.replicaNode())(TableName.AuditLog) + .where(`${TableName.AuditLog}.orgId`, orgId) + .whereRaw(`"${TableName.AuditLog}"."createdAt" >= ?::timestamptz`, [startDate]) + .andWhereRaw(`"${TableName.AuditLog}"."createdAt" < ?::timestamptz`, [endDate]) // eslint-disable-next-line func-names .where(function () { - if (orgId) { - void this.where(`${TableName.AuditLog}.orgId`, orgId); - } else if (projectId) { + if (projectId) { void this.where(`${TableName.AuditLog}.projectId`, projectId); } }); @@ -135,14 +132,6 @@ export const auditLogDALFactory = (db: TDbClient) => { void sqlQuery.whereIn("eventType", eventType); } - // Filter by date range - if (startDate) { - void sqlQuery.whereRaw(`"${TableName.AuditLog}"."createdAt" >= ?::timestamptz`, [startDate]); - } - if (endDate) { - void sqlQuery.whereRaw(`"${TableName.AuditLog}"."createdAt" <= ?::timestamptz`, [endDate]); - } - // we timeout long running queries to prevent DB resource issues (2 minutes) const docs = await sqlQuery.timeout(1000 * 120); @@ -174,6 +163,8 @@ export const auditLogDALFactory = (db: TDbClient) => { try { const findExpiredLogSubQuery = (tx || db)(TableName.AuditLog) .where("expiresAt", "<", today) + .where("createdAt", "<", today) // to use audit log partition + .orderBy(`${TableName.AuditLog}.createdAt`, "desc") .select("id") .limit(AUDIT_LOG_PRUNE_BATCH_SIZE); diff --git a/backend/src/ee/services/audit-log/audit-log-service.ts b/backend/src/ee/services/audit-log/audit-log-service.ts index 4bea26ac6..333847734 100644 --- a/backend/src/ee/services/audit-log/audit-log-service.ts +++ b/backend/src/ee/services/audit-log/audit-log-service.ts @@ -1,7 +1,6 @@ import { ForbiddenError } from "@casl/ability"; import { requestContext } from "@fastify/request-context"; -import { ActionProjectType } from "@app/db/schemas"; import { getConfig } from "@app/lib/config/env"; import { BadRequestError } from "@app/lib/errors"; import { ActorType } from "@app/services/auth/auth-type"; @@ -38,8 +37,7 @@ export const auditLogServiceFactory = ({ actorId, projectId: filter.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs); } else { @@ -69,7 +67,8 @@ export const auditLogServiceFactory = ({ secretPath: filter.secretPath, secretKey: filter.secretKey, environment: filter.environment, - ...(filter.projectId ? { projectId: filter.projectId } : { orgId: actorOrgId }) + orgId: actorOrgId, + ...(filter.projectId ? { projectId: filter.projectId } : {}) }); return auditLogs.map(({ eventType: logEventType, actor: eActor, actorMetadata, eventMetadata, ...el }) => ({ diff --git a/backend/src/ee/services/audit-log/audit-log-types.ts b/backend/src/ee/services/audit-log/audit-log-types.ts index a2acb62c4..625dd6556 100644 --- a/backend/src/ee/services/audit-log/audit-log-types.ts +++ b/backend/src/ee/services/audit-log/audit-log-types.ts @@ -56,8 +56,8 @@ export type TListProjectAuditLogDTO = { eventType?: EventType[]; offset?: number; limit: number; - endDate?: string; - startDate?: string; + endDate: string; + startDate: string; projectId?: string; environment?: string; auditLogActorId?: string; diff --git a/backend/src/ee/services/certificate-authority-crl/certificate-authority-crl-service.ts b/backend/src/ee/services/certificate-authority-crl/certificate-authority-crl-service.ts index 5ead798fa..cc6a6b5fe 100644 --- a/backend/src/ee/services/certificate-authority-crl/certificate-authority-crl-service.ts +++ b/backend/src/ee/services/certificate-authority-crl/certificate-authority-crl-service.ts @@ -1,7 +1,6 @@ import { ForbiddenError } from "@casl/ability"; import * as x509 from "@peculiar/x509"; -import { ActionProjectType } from "@app/db/schemas"; import { TCertificateAuthorityCrlDALFactory } from "@app/ee/services/certificate-authority-crl/certificate-authority-crl-dal"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; @@ -78,8 +77,7 @@ export const certificateAuthorityCrlServiceFactory = ({ actorId, projectId: ca.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( diff --git a/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-service.ts b/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-service.ts index cf37626c7..c2c596922 100644 --- a/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-service.ts +++ b/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-service.ts @@ -1,7 +1,6 @@ import { ForbiddenError, subject } from "@casl/ability"; import RE2 from "re2"; -import { ActionProjectType } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { @@ -85,8 +84,7 @@ export const dynamicSecretLeaseServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const plan = await licenseService.getPlan(actorOrgId); @@ -202,8 +200,7 @@ export const dynamicSecretLeaseServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({ @@ -300,8 +297,7 @@ export const dynamicSecretLeaseServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({ @@ -389,8 +385,7 @@ export const dynamicSecretLeaseServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path); @@ -437,8 +432,7 @@ export const dynamicSecretLeaseServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path); diff --git a/backend/src/ee/services/dynamic-secret/dynamic-secret-service.ts b/backend/src/ee/services/dynamic-secret/dynamic-secret-service.ts index d0d14ddaf..bfa39f39b 100644 --- a/backend/src/ee/services/dynamic-secret/dynamic-secret-service.ts +++ b/backend/src/ee/services/dynamic-secret/dynamic-secret-service.ts @@ -1,6 +1,5 @@ import { ForbiddenError, subject } from "@casl/ability"; -import { ActionProjectType } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { @@ -78,8 +77,7 @@ export const dynamicSecretServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -202,8 +200,7 @@ export const dynamicSecretServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const plan = await licenseService.getPlan(actorOrgId); @@ -354,8 +351,7 @@ export const dynamicSecretServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path); @@ -420,8 +416,7 @@ export const dynamicSecretServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path); @@ -485,8 +480,7 @@ export const dynamicSecretServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); // verify user has access to each env in request @@ -529,8 +523,7 @@ export const dynamicSecretServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionDynamicSecretActions.ReadRootCredential, @@ -578,8 +571,7 @@ export const dynamicSecretServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path); @@ -616,8 +608,7 @@ export const dynamicSecretServiceFactory = ({ actorId: actor.id, projectId, actorAuthMethod: actor.authMethod, - actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId: actor.orgId }); const userAccessibleFolderMappings = folderMappings.filter(({ path, environment }) => @@ -661,8 +652,7 @@ export const dynamicSecretServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const folders = await folderDAL.findBySecretPathMultiEnv(projectId, environmentSlugs, path); diff --git a/backend/src/ee/services/identity-project-additional-privilege-v2/identity-project-additional-privilege-v2-service.ts b/backend/src/ee/services/identity-project-additional-privilege-v2/identity-project-additional-privilege-v2-service.ts index 64da588f8..485e46885 100644 --- a/backend/src/ee/services/identity-project-additional-privilege-v2/identity-project-additional-privilege-v2-service.ts +++ b/backend/src/ee/services/identity-project-additional-privilege-v2/identity-project-additional-privilege-v2-service.ts @@ -1,7 +1,7 @@ import { ForbiddenError, subject } from "@casl/ability"; import { packRules } from "@casl/ability/extra"; -import { ActionProjectType, TableName } from "@app/db/schemas"; +import { TableName } from "@app/db/schemas"; import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors"; import { ms } from "@app/lib/ms"; import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars"; @@ -61,8 +61,7 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({ actorId, projectId: identityProjectMembership.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionIdentityActions.Edit, @@ -73,8 +72,7 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({ actorId: identityId, projectId: identityProjectMembership.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); // we need to validate that the privilege given is not higher than the assigning users permission @@ -160,8 +158,7 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({ actorId, projectId: identityProjectMembership.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionIdentityActions.Edit, @@ -172,8 +169,7 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({ actorId: identityProjectMembership.identityId, projectId: identityProjectMembership.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); // we need to validate that the privilege given is not higher than the assigning users permission @@ -260,8 +256,7 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({ actorId, projectId: identityProjectMembership.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionIdentityActions.Edit, @@ -272,8 +267,7 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({ actorId: identityProjectMembership.identityId, projectId: identityProjectMembership.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); const permissionBoundary = validatePrivilegeChangeOperation( membership.shouldUseNewPrivilegeSystem, @@ -321,8 +315,7 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({ actorId, projectId: identityProjectMembership.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionIdentityActions.Read, @@ -356,8 +349,7 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({ actorId, projectId: identityProjectMembership.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionIdentityActions.Read, @@ -392,8 +384,7 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({ actorId, projectId: identityProjectMembership.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionIdentityActions.Read, diff --git a/backend/src/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service.ts b/backend/src/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service.ts index 828cf43a3..747e13f15 100644 --- a/backend/src/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service.ts +++ b/backend/src/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service.ts @@ -1,7 +1,6 @@ import { ForbiddenError, MongoAbility, RawRuleOf, subject } from "@casl/ability"; import { PackRule, packRules, unpackRules } from "@casl/ability/extra"; -import { ActionProjectType } from "@app/db/schemas"; import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors"; import { ms } from "@app/lib/ms"; import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars"; @@ -73,8 +72,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ actorId, projectId: identityProjectMembership.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -87,8 +85,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ actorId: identityId, projectId: identityProjectMembership.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); // we need to validate that the privilege given is not higher than the assigning users permission @@ -175,8 +172,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ actorId, projectId: identityProjectMembership.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -189,8 +185,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ actorId: identityProjectMembership.identityId, projectId: identityProjectMembership.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); // we need to validate that the privilege given is not higher than the assigning users permission @@ -293,8 +288,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ actorId, projectId: identityProjectMembership.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionIdentityActions.Edit, @@ -306,8 +300,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ actorId: identityProjectMembership.identityId, projectId: identityProjectMembership.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); const permissionBoundary = validatePrivilegeChangeOperation( membership.shouldUseNewPrivilegeSystem, @@ -366,8 +359,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ actorId, projectId: identityProjectMembership.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionIdentityActions.Read, @@ -409,8 +401,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ actorId, projectId: identityProjectMembership.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( diff --git a/backend/src/ee/services/kmip/kmip-operation-service.ts b/backend/src/ee/services/kmip/kmip-operation-service.ts index 55d8c2b42..2808108df 100644 --- a/backend/src/ee/services/kmip/kmip-operation-service.ts +++ b/backend/src/ee/services/kmip/kmip-operation-service.ts @@ -24,7 +24,7 @@ type TKmipOperationServiceFactoryDep = { kmsService: TKmsServiceFactory; kmsDAL: TKmsKeyDALFactory; kmipClientDAL: TKmipClientDALFactory; - projectDAL: Pick; + projectDAL: Pick; permissionService: Pick; }; diff --git a/backend/src/ee/services/kmip/kmip-service.ts b/backend/src/ee/services/kmip/kmip-service.ts index 618e67973..44d970fa3 100644 --- a/backend/src/ee/services/kmip/kmip-service.ts +++ b/backend/src/ee/services/kmip/kmip-service.ts @@ -1,7 +1,6 @@ import { ForbiddenError } from "@casl/ability"; import * as x509 from "@peculiar/x509"; -import { ActionProjectType } from "@app/db/schemas"; import { crypto } from "@app/lib/crypto/cryptography"; import { BadRequestError, InternalServerError, NotFoundError } from "@app/lib/errors"; import { isValidIp } from "@app/lib/ip"; @@ -73,8 +72,7 @@ export const kmipServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.KMS + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -127,8 +125,7 @@ export const kmipServiceFactory = ({ actorId, projectId: kmipClient.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.KMS + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -159,8 +156,7 @@ export const kmipServiceFactory = ({ actorId, projectId: kmipClient.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.KMS + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -193,8 +189,7 @@ export const kmipServiceFactory = ({ actorId, projectId: kmipClient.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.KMS + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionKmipActions.ReadClients, ProjectPermissionSub.Kmip); @@ -215,8 +210,7 @@ export const kmipServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.KMS + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionKmipActions.ReadClients, ProjectPermissionSub.Kmip); @@ -252,8 +246,7 @@ export const kmipServiceFactory = ({ actorId, projectId: kmipClient.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.KMS + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( diff --git a/backend/src/ee/services/oidc/oidc-config-service.ts b/backend/src/ee/services/oidc/oidc-config-service.ts index a5088bde5..1a3374035 100644 --- a/backend/src/ee/services/oidc/oidc-config-service.ts +++ b/backend/src/ee/services/oidc/oidc-config-service.ts @@ -107,34 +107,26 @@ export const oidcConfigServiceFactory = ({ kmsService }: TOidcConfigServiceFactoryDep) => { const getOidc = async (dto: TGetOidcCfgDTO) => { - const org = await orgDAL.findOne({ slug: dto.orgSlug }); - if (!org) { + const oidcCfg = await oidcConfigDAL.findOne({ + orgId: dto.organizationId + }); + if (!oidcCfg) { throw new NotFoundError({ - message: `Organization with slug '${dto.orgSlug}' not found`, - name: "OrgNotFound" + message: `OIDC configuration for organization with ID '${dto.organizationId}' not found` }); } + if (dto.type === "external") { const { permission } = await permissionService.getOrgPermission( dto.actor, dto.actorId, - org.id, + dto.organizationId, dto.actorAuthMethod, dto.actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Sso); } - const oidcCfg = await oidcConfigDAL.findOne({ - orgId: org.id - }); - - if (!oidcCfg) { - throw new NotFoundError({ - message: `OIDC configuration for organization with slug '${dto.orgSlug}' not found` - }); - } - const { decryptor } = await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.Organization, orgId: oidcCfg.orgId @@ -465,7 +457,7 @@ export const oidcConfigServiceFactory = ({ }; const updateOidcCfg = async ({ - orgSlug, + organizationId, allowedEmailDomains, configurationType, discoveryURL, @@ -484,13 +476,11 @@ export const oidcConfigServiceFactory = ({ manageGroupMemberships, jwtSignatureAlgorithm }: TUpdateOidcCfgDTO) => { - const org = await orgDAL.findOne({ - slug: orgSlug - }); + const org = await orgDAL.findOne({ id: organizationId }); if (!org) { throw new NotFoundError({ - message: `Organization with slug '${orgSlug}' not found` + message: `Organization with ID '${organizationId}' not found` }); } @@ -555,7 +545,7 @@ export const oidcConfigServiceFactory = ({ }; const createOidcCfg = async ({ - orgSlug, + organizationId, allowedEmailDomains, configurationType, discoveryURL, @@ -574,12 +564,10 @@ export const oidcConfigServiceFactory = ({ manageGroupMemberships, jwtSignatureAlgorithm }: TCreateOidcCfgDTO) => { - const org = await orgDAL.findOne({ - slug: orgSlug - }); + const org = await orgDAL.findOne({ id: organizationId }); if (!org) { throw new NotFoundError({ - message: `Organization with slug '${orgSlug}' not found` + message: `Organization with ID '${organizationId}' not found` }); } @@ -639,7 +627,7 @@ export const oidcConfigServiceFactory = ({ const oidcCfg = await getOidc({ type: "internal", - orgSlug + organizationId: org.id }); if (!oidcCfg || !oidcCfg.isActive) { diff --git a/backend/src/ee/services/oidc/oidc-config-types.ts b/backend/src/ee/services/oidc/oidc-config-types.ts index c56427e63..f38f2172f 100644 --- a/backend/src/ee/services/oidc/oidc-config-types.ts +++ b/backend/src/ee/services/oidc/oidc-config-types.ts @@ -26,11 +26,11 @@ export type TOidcLoginDTO = { export type TGetOidcCfgDTO = | ({ type: "external"; - orgSlug: string; + organizationId: string; } & TGenericPermission) | { type: "internal"; - orgSlug: string; + organizationId: string; }; export type TCreateOidcCfgDTO = { @@ -45,7 +45,7 @@ export type TCreateOidcCfgDTO = { clientId: string; clientSecret: string; isActive: boolean; - orgSlug: string; + organizationId: string; manageGroupMemberships: boolean; jwtSignatureAlgorithm: OIDCJWTSignatureAlgorithm; } & TGenericPermission; @@ -62,7 +62,7 @@ export type TUpdateOidcCfgDTO = Partial<{ clientId: string; clientSecret: string; isActive: boolean; - orgSlug: string; + organizationId: string; manageGroupMemberships: boolean; jwtSignatureAlgorithm: OIDCJWTSignatureAlgorithm; }> & diff --git a/backend/src/ee/services/permission/permission-dal.ts b/backend/src/ee/services/permission/permission-dal.ts index 11ee29852..9677c69b1 100644 --- a/backend/src/ee/services/permission/permission-dal.ts +++ b/backend/src/ee/services/permission/permission-dal.ts @@ -91,7 +91,7 @@ export interface TPermissionDALFactory { userId: string; projectId: string; username: string; - projectType: string; + projectType?: string | null; id: string; createdAt: Date; updatedAt: Date; @@ -163,7 +163,7 @@ export interface TPermissionDALFactory { createdAt: Date; updatedAt: Date; orgId: string; - projectType: string; + projectType?: string | null; shouldUseNewPrivilegeSystem: boolean; orgAuthEnforced: boolean; metadata: { @@ -201,7 +201,7 @@ export interface TPermissionDALFactory { userId: string; projectId: string; username: string; - projectType: string; + projectType?: string | null; id: string; createdAt: Date; updatedAt: Date; @@ -267,7 +267,7 @@ export interface TPermissionDALFactory { createdAt: Date; updatedAt: Date; orgId: string; - projectType: string; + projectType?: string | null; orgAuthEnforced: boolean; metadata: { id: string; diff --git a/backend/src/ee/services/permission/permission-service-types.ts b/backend/src/ee/services/permission/permission-service-types.ts index 5e71c65d9..72df88982 100644 --- a/backend/src/ee/services/permission/permission-service-types.ts +++ b/backend/src/ee/services/permission/permission-service-types.ts @@ -1,7 +1,6 @@ import { MongoAbility, RawRuleOf } from "@casl/ability"; import { MongoQuery } from "@ucast/mongo2js"; -import { ActionProjectType } from "@app/db/schemas"; import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type"; import { OrgPermissionSet } from "./org-permission"; @@ -21,7 +20,6 @@ export type TGetUserProjectPermissionArg = { userId: string; projectId: string; authMethod: ActorAuthMethod; - actionProjectType: ActionProjectType; userOrgId?: string; }; @@ -29,14 +27,12 @@ export type TGetIdentityProjectPermissionArg = { identityId: string; projectId: string; identityOrgId?: string; - actionProjectType: ActionProjectType; }; export type TGetServiceTokenProjectPermissionArg = { serviceTokenId: string; projectId: string; actorOrgId?: string; - actionProjectType: ActionProjectType; }; export type TGetProjectPermissionArg = { @@ -45,7 +41,6 @@ export type TGetProjectPermissionArg = { projectId: string; actorAuthMethod: ActorAuthMethod; actorOrgId?: string; - actionProjectType: ActionProjectType; }; export type TPermissionServiceFactory = { @@ -143,13 +138,7 @@ export type TPermissionServiceFactory = { }; } >; - getUserProjectPermission: ({ - userId, - projectId, - authMethod, - userOrgId, - actionProjectType - }: TGetUserProjectPermissionArg) => Promise<{ + getUserProjectPermission: ({ userId, projectId, authMethod, userOrgId }: TGetUserProjectPermissionArg) => Promise<{ permission: MongoAbility; membership: { id: string; diff --git a/backend/src/ee/services/permission/permission-service.ts b/backend/src/ee/services/permission/permission-service.ts index 85ee82cca..32c01dcfb 100644 --- a/backend/src/ee/services/permission/permission-service.ts +++ b/backend/src/ee/services/permission/permission-service.ts @@ -5,7 +5,6 @@ import { MongoQuery } from "@ucast/mongo2js"; import handlebars from "handlebars"; import { - ActionProjectType, OrgMembershipRole, ProjectMembershipRole, ServiceTokenScopes, @@ -214,8 +213,7 @@ export const permissionServiceFactory = ({ userId, projectId, authMethod, - userOrgId, - actionProjectType + userOrgId }: TGetUserProjectPermissionArg): Promise> => { const userProjectPermission = await permissionDAL.getProjectPermission(userId, projectId); if (!userProjectPermission) throw new ForbiddenRequestError({ name: "User not a part of the specified project" }); @@ -242,12 +240,6 @@ export const permissionServiceFactory = ({ userProjectPermission.orgRole ); - if (actionProjectType !== ActionProjectType.Any && actionProjectType !== userProjectPermission.projectType) { - throw new BadRequestError({ - message: `The project is of type ${userProjectPermission.projectType}. Operations of type ${actionProjectType} are not allowed.` - }); - } - // join two permissions and pass to build the final permission set const rolePermissions = userProjectPermission.roles?.map(({ role, permissions }) => ({ role, permissions })) || []; const additionalPrivileges = @@ -295,8 +287,7 @@ export const permissionServiceFactory = ({ const getIdentityProjectPermission = async ({ identityId, projectId, - identityOrgId, - actionProjectType + identityOrgId }: TGetIdentityProjectPermissionArg): Promise> => { const identityProjectPermission = await permissionDAL.getProjectIdentityPermission(identityId, projectId); if (!identityProjectPermission) @@ -316,12 +307,6 @@ export const permissionServiceFactory = ({ throw new ForbiddenRequestError({ name: "Identity is not a member of the specified organization" }); } - if (actionProjectType !== ActionProjectType.Any && actionProjectType !== identityProjectPermission.projectType) { - throw new BadRequestError({ - message: `The project is of type ${identityProjectPermission.projectType}. Operations of type ${actionProjectType} are not allowed.` - }); - } - const rolePermissions = identityProjectPermission.roles?.map(({ role, permissions }) => ({ role, permissions })) || []; const additionalPrivileges = @@ -376,8 +361,7 @@ export const permissionServiceFactory = ({ const getServiceTokenProjectPermission = async ({ serviceTokenId, projectId, - actorOrgId, - actionProjectType + actorOrgId }: TGetServiceTokenProjectPermissionArg) => { const serviceToken = await serviceTokenDAL.findById(serviceTokenId); if (!serviceToken) throw new NotFoundError({ message: `Service token with ID '${serviceTokenId}' not found` }); @@ -402,12 +386,6 @@ export const permissionServiceFactory = ({ }); } - if (actionProjectType !== ActionProjectType.Any && actionProjectType !== serviceTokenProject.type) { - throw new BadRequestError({ - message: `The project is of type ${serviceTokenProject.type}. Operations of type ${actionProjectType} are not allowed.` - }); - } - const scopes = ServiceTokenScopes.parse(serviceToken.scopes || []); return { permission: buildServiceTokenProjectPermission(scopes, serviceToken.permissions), @@ -559,8 +537,7 @@ export const permissionServiceFactory = ({ actorId: inputActorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType + actorOrgId }: TGetProjectPermissionArg): Promise> => { let actor = inputActor; let actorId = inputActorId; @@ -581,22 +558,19 @@ export const permissionServiceFactory = ({ userId: actorId, projectId, authMethod: actorAuthMethod, - userOrgId: actorOrgId, - actionProjectType + userOrgId: actorOrgId }) as Promise>; case ActorType.SERVICE: return getServiceTokenProjectPermission({ serviceTokenId: actorId, projectId, - actorOrgId, - actionProjectType + actorOrgId }) as Promise>; case ActorType.IDENTITY: return getIdentityProjectPermission({ identityId: actorId, projectId, - identityOrgId: actorOrgId, - actionProjectType + identityOrgId: actorOrgId }) as Promise>; default: throw new BadRequestError({ diff --git a/backend/src/ee/services/pit/pit-service.ts b/backend/src/ee/services/pit/pit-service.ts index 0eb223fb4..c2a485b35 100644 --- a/backend/src/ee/services/pit/pit-service.ts +++ b/backend/src/ee/services/pit/pit-service.ts @@ -1,7 +1,6 @@ /* eslint-disable no-await-in-loop */ import { ForbiddenError } from "@casl/ability"; -import { ActionProjectType } from "@app/db/schemas"; import { ProjectPermissionCommitsActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { NotFoundError } from "@app/lib/errors"; import { logger } from "@app/lib/logger"; @@ -321,8 +320,7 @@ export const pitServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(userPermission).throwUnlessCan( diff --git a/backend/src/ee/services/project-template/project-template-fns.ts b/backend/src/ee/services/project-template/project-template-fns.ts index 8e8ebfa13..5d4d0a953 100644 --- a/backend/src/ee/services/project-template/project-template-fns.ts +++ b/backend/src/ee/services/project-template/project-template-fns.ts @@ -1,4 +1,3 @@ -import { ProjectType } from "@app/db/schemas"; import { InfisicalProjectTemplate, TUnpackedPermission @@ -7,21 +6,18 @@ import { getPredefinedRoles } from "@app/services/project-role/project-role-fns" import { ProjectTemplateDefaultEnvironments } from "./project-template-constants"; -export const getDefaultProjectTemplate = (orgId: string, type: ProjectType) => ({ +export const getDefaultProjectTemplate = (orgId: string) => ({ id: "b11b49a9-09a9-4443-916a-4246f9ff2c69", // random ID to appease zod - type, name: InfisicalProjectTemplate.Default, createdAt: new Date(), updatedAt: new Date(), - description: `Infisical's ${type} default project template`, - environments: type === ProjectType.SecretManager ? ProjectTemplateDefaultEnvironments : null, - roles: [...getPredefinedRoles({ projectId: "project-template", projectType: type })].map( - ({ name, slug, permissions }) => ({ - name, - slug, - permissions: permissions as TUnpackedPermission[] - }) - ), + description: `Infisical's default project template`, + environments: ProjectTemplateDefaultEnvironments, + roles: getPredefinedRoles({ projectId: "project-template" }) as Array<{ + name: string; + slug: string; + permissions: TUnpackedPermission[]; + }>, orgId }); diff --git a/backend/src/ee/services/project-template/project-template-service.ts b/backend/src/ee/services/project-template/project-template-service.ts index 9d585fc9a..510105572 100644 --- a/backend/src/ee/services/project-template/project-template-service.ts +++ b/backend/src/ee/services/project-template/project-template-service.ts @@ -1,7 +1,7 @@ import { ForbiddenError } from "@casl/ability"; import { packRules } from "@casl/ability/extra"; -import { ProjectType, TProjectTemplates } from "@app/db/schemas"; +import { TProjectTemplates } from "@app/db/schemas"; 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-types"; @@ -29,13 +29,11 @@ const $unpackProjectTemplate = ({ roles, environments, ...rest }: TProjectTempla ...rest, environments: environments as TProjectTemplateEnvironment[], roles: [ - ...getPredefinedRoles({ projectId: "project-template", projectType: rest.type as ProjectType }).map( - ({ name, slug, permissions }) => ({ - name, - slug, - permissions: permissions as TUnpackedPermission[] - }) - ), + ...getPredefinedRoles({ projectId: "project-template" }).map(({ name, slug, permissions }) => ({ + name, + slug, + permissions: permissions as TUnpackedPermission[] + })), ...(roles as TProjectTemplateRole[]).map((role) => ({ ...role, permissions: unpackPermissions(role.permissions) @@ -48,10 +46,7 @@ export const projectTemplateServiceFactory = ({ permissionService, projectTemplateDAL }: TProjectTemplatesServiceFactoryDep): TProjectTemplateServiceFactory => { - const listProjectTemplatesByOrg: TProjectTemplateServiceFactory["listProjectTemplatesByOrg"] = async ( - actor, - type - ) => { + const listProjectTemplatesByOrg: TProjectTemplateServiceFactory["listProjectTemplatesByOrg"] = async (actor) => { const plan = await licenseService.getPlan(actor.orgId); if (!plan.projectTemplates) @@ -70,14 +65,11 @@ export const projectTemplateServiceFactory = ({ ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.ProjectTemplates); const projectTemplates = await projectTemplateDAL.find({ - orgId: actor.orgId, - ...(type ? { type } : {}) + orgId: actor.orgId }); return [ - ...(type - ? [getDefaultProjectTemplate(actor.orgId, type)] - : Object.values(ProjectType).map((projectType) => getDefaultProjectTemplate(actor.orgId, projectType))), + getDefaultProjectTemplate(actor.orgId), ...projectTemplates.map((template) => $unpackProjectTemplate(template)) ]; }; @@ -142,7 +134,7 @@ export const projectTemplateServiceFactory = ({ }; const createProjectTemplate: TProjectTemplateServiceFactory["createProjectTemplate"] = async ( - { roles, environments, type, ...params }, + { roles, environments, ...params }, actor ) => { const plan = await licenseService.getPlan(actor.orgId); @@ -162,10 +154,6 @@ export const projectTemplateServiceFactory = ({ ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.ProjectTemplates); - if (environments && type !== ProjectType.SecretManager) { - throw new BadRequestError({ message: "Cannot configure environments for non-SecretManager project templates" }); - } - if (environments && plan.environmentLimit !== null && environments.length > plan.environmentLimit) { throw new BadRequestError({ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions @@ -188,10 +176,8 @@ export const projectTemplateServiceFactory = ({ const projectTemplate = await projectTemplateDAL.create({ ...params, roles: JSON.stringify(roles.map((role) => ({ ...role, permissions: packRules(role.permissions) }))), - environments: - type === ProjectType.SecretManager ? JSON.stringify(environments ?? ProjectTemplateDefaultEnvironments) : null, - orgId: actor.orgId, - type + environments: environments ? JSON.stringify(environments ?? ProjectTemplateDefaultEnvironments) : null, + orgId: actor.orgId }); return $unpackProjectTemplate(projectTemplate); @@ -223,12 +209,6 @@ export const projectTemplateServiceFactory = ({ ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.ProjectTemplates); - if (projectTemplate.type !== ProjectType.SecretManager && environments) - throw new BadRequestError({ message: "Cannot configure environments for non-SecretManager project templates" }); - - if (projectTemplate.type === ProjectType.SecretManager && environments === null) - throw new BadRequestError({ message: "Environments cannot be removed for SecretManager project templates" }); - if (environments && plan.environmentLimit !== null && environments.length > plan.environmentLimit) { throw new BadRequestError({ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions diff --git a/backend/src/ee/services/project-template/project-template-types.ts b/backend/src/ee/services/project-template/project-template-types.ts index 2684e10e5..e705a096d 100644 --- a/backend/src/ee/services/project-template/project-template-types.ts +++ b/backend/src/ee/services/project-template/project-template-types.ts @@ -1,6 +1,6 @@ import { z } from "zod"; -import { ProjectMembershipRole, ProjectType, TProjectEnvironments } from "@app/db/schemas"; +import { ProjectMembershipRole, TProjectEnvironments } from "@app/db/schemas"; import { TProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission"; import { OrgServiceActor } from "@app/lib/types"; import { UnpackedPermissionSchema } from "@app/server/routes/sanitizedSchema/permission"; @@ -16,7 +16,6 @@ export type TProjectTemplateRole = { export type TCreateProjectTemplateDTO = { name: string; description?: string; - type: ProjectType; roles: TProjectTemplateRole[]; environments?: TProjectTemplateEnvironment[] | null; }; @@ -30,14 +29,10 @@ export enum InfisicalProjectTemplate { } export type TProjectTemplateServiceFactory = { - listProjectTemplatesByOrg: ( - actor: OrgServiceActor, - type?: ProjectType - ) => Promise< + listProjectTemplatesByOrg: (actor: OrgServiceActor) => Promise< ( | { id: string; - type: ProjectType; name: InfisicalProjectTemplate; createdAt: Date; updatedAt: Date; @@ -74,7 +69,6 @@ export type TProjectTemplateServiceFactory = { name: string; }[]; name: string; - type: string; orgId: string; id: string; createdAt: Date; @@ -99,7 +93,6 @@ export type TProjectTemplateServiceFactory = { name: string; }[]; name: string; - type: string; orgId: string; id: string; createdAt: Date; @@ -123,7 +116,6 @@ export type TProjectTemplateServiceFactory = { name: string; }[]; name: string; - type: string; orgId: string; id: string; createdAt: Date; @@ -146,7 +138,6 @@ export type TProjectTemplateServiceFactory = { name: string; }[]; name: string; - type: string; orgId: string; id: string; createdAt: Date; @@ -170,7 +161,6 @@ export type TProjectTemplateServiceFactory = { name: string; }[]; name: string; - type: string; orgId: string; id: string; createdAt: Date; @@ -194,7 +184,6 @@ export type TProjectTemplateServiceFactory = { name: string; }[]; name: string; - type: string; orgId: string; id: string; createdAt: Date; diff --git a/backend/src/ee/services/project-user-additional-privilege/project-user-additional-privilege-service.ts b/backend/src/ee/services/project-user-additional-privilege/project-user-additional-privilege-service.ts index 944775156..d44ab054d 100644 --- a/backend/src/ee/services/project-user-additional-privilege/project-user-additional-privilege-service.ts +++ b/backend/src/ee/services/project-user-additional-privilege/project-user-additional-privilege-service.ts @@ -1,7 +1,7 @@ import { ForbiddenError, MongoAbility, RawRuleOf } from "@casl/ability"; import { PackRule, packRules, unpackRules } from "@casl/ability/extra"; -import { ActionProjectType, TableName } from "@app/db/schemas"; +import { TableName } from "@app/db/schemas"; import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors"; import { ms } from "@app/lib/ms"; import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars"; @@ -61,8 +61,7 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({ actorId, projectId: projectMembership.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Edit, ProjectPermissionSub.Member); const { permission: targetUserPermission, membership } = await permissionService.getProjectPermission({ @@ -70,8 +69,7 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({ actorId: projectMembership.userId, projectId: projectMembership.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); // we need to validate that the privilege given is not higher than the assigning users permission @@ -166,8 +164,7 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({ actorId, projectId: projectMembership.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Edit, ProjectPermissionSub.Member); const { permission: targetUserPermission } = await permissionService.getProjectPermission({ @@ -175,8 +172,7 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({ actorId: projectMembership.userId, projectId: projectMembership.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); // we need to validate that the privilege given is not higher than the assigning users permission @@ -276,8 +272,7 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({ actorId, projectId: projectMembership.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Edit, ProjectPermissionSub.Member); @@ -322,8 +317,7 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({ actorId, projectId: projectMembership.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Read, ProjectPermissionSub.Member); @@ -349,8 +343,7 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({ actorId, projectId: projectMembership.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Read, ProjectPermissionSub.Member); diff --git a/backend/src/ee/services/saml-config/saml-config-service.ts b/backend/src/ee/services/saml-config/saml-config-service.ts index c81fd518b..5430b0afc 100644 --- a/backend/src/ee/services/saml-config/saml-config-service.ts +++ b/backend/src/ee/services/saml-config/saml-config-service.ts @@ -148,10 +148,18 @@ export const samlConfigServiceFactory = ({ let samlConfig: TSamlConfigs | undefined; if (dto.type === "org") { samlConfig = await samlConfigDAL.findOne({ orgId: dto.orgId }); - if (!samlConfig) return; + if (!samlConfig) { + throw new NotFoundError({ + message: `SAML configuration for organization with ID '${dto.orgId}' not found` + }); + } } else if (dto.type === "orgSlug") { const org = await orgDAL.findOne({ slug: dto.orgSlug }); - if (!org) return; + if (!org) { + throw new NotFoundError({ + message: `Organization with slug '${dto.orgSlug}' not found` + }); + } samlConfig = await samlConfigDAL.findOne({ orgId: org.id }); } else if (dto.type === "ssoId") { // TODO: diff --git a/backend/src/ee/services/saml-config/saml-config-types.ts b/backend/src/ee/services/saml-config/saml-config-types.ts index a9bd8f485..f4ede04fa 100644 --- a/backend/src/ee/services/saml-config/saml-config-types.ts +++ b/backend/src/ee/services/saml-config/saml-config-types.ts @@ -61,20 +61,17 @@ export type TSamlLoginDTO = { export type TSamlConfigServiceFactory = { createSamlCfg: (arg: TCreateSamlCfgDTO) => Promise; updateSamlCfg: (arg: TUpdateSamlCfgDTO) => Promise; - getSaml: (arg: TGetSamlCfgDTO) => Promise< - | { - id: string; - organization: string; - orgId: string; - authProvider: string; - isActive: boolean; - entryPoint: string; - issuer: string; - cert: string; - lastUsed: Date | null | undefined; - } - | undefined - >; + getSaml: (arg: TGetSamlCfgDTO) => Promise<{ + id: string; + organization: string; + orgId: string; + authProvider: string; + isActive: boolean; + entryPoint: string; + issuer: string; + cert: string; + lastUsed: Date | null | undefined; + }>; samlLogin: (arg: TSamlLoginDTO) => Promise<{ isUserCompleted: boolean; providerAuthToken: string; diff --git a/backend/src/ee/services/secret-approval-policy/secret-approval-policy-service.ts b/backend/src/ee/services/secret-approval-policy/secret-approval-policy-service.ts index c8df810ed..cb497aa7d 100644 --- a/backend/src/ee/services/secret-approval-policy/secret-approval-policy-service.ts +++ b/backend/src/ee/services/secret-approval-policy/secret-approval-policy-service.ts @@ -1,7 +1,6 @@ import { ForbiddenError } from "@casl/ability"; import picomatch from "picomatch"; -import { ActionProjectType } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { BadRequestError, NotFoundError } from "@app/lib/errors"; @@ -91,8 +90,7 @@ export const secretApprovalPolicyServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, @@ -267,8 +265,7 @@ export const secretApprovalPolicyServiceFactory = ({ actorId, projectId: secretApprovalPolicy.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval); @@ -423,8 +420,7 @@ export const secretApprovalPolicyServiceFactory = ({ actorId, projectId: sapPolicy.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, @@ -463,8 +459,7 @@ export const secretApprovalPolicyServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval); @@ -508,8 +503,7 @@ export const secretApprovalPolicyServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); return getSecretApprovalPolicy(projectId, environment, secretPath); @@ -535,8 +529,7 @@ export const secretApprovalPolicyServiceFactory = ({ actorId, projectId: sapPolicy.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval); diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-dal.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-dal.ts index 5e1e546d6..ec6a17d97 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-dal.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-dal.ts @@ -290,7 +290,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => { } }; - const findProjectRequestCount = async (projectId: string, userId: string, tx?: Knex) => { + const findProjectRequestCount = async (projectId: string, userId: string, policyId?: string, tx?: Knex) => { try { const docs = await (tx || db) .with( @@ -309,6 +309,9 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => { `${TableName.SecretApprovalPolicy}.id` ) .where({ projectId }) + .where((qb) => { + if (policyId) void qb.where(`${TableName.SecretApprovalPolicy}.id`, policyId); + }) .andWhere( (bd) => void bd diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-fns.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-fns.ts index 58a39dfa7..5e4e0e3d6 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-fns.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-fns.ts @@ -36,7 +36,7 @@ export const sendApprovalEmailsFn = async ({ firstName: reviewerUser.firstName, projectName: project.name, organizationName: project.organization.name, - approvalUrl: `${cfg.SITE_URL}/secret-manager/${project.id}/approval?requestId=${secretApprovalRequest.id}` + approvalUrl: `${cfg.SITE_URL}/projects/${project.id}/secret-manager/approval?requestId=${secretApprovalRequest.id}` }, template: SmtpTemplates.SecretApprovalRequestNeedsReview }); diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts index 130cd184f..c21150b75 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts @@ -2,7 +2,6 @@ import { ForbiddenError, subject } from "@casl/ability"; import { - ActionProjectType, ProjectMembershipRole, SecretEncryptionAlgo, SecretKeyEncoding, @@ -168,7 +167,14 @@ export const secretApprovalRequestServiceFactory = ({ microsoftTeamsService, folderCommitService }: TSecretApprovalRequestServiceFactoryDep) => { - const requestCount = async ({ projectId, actor, actorId, actorOrgId, actorAuthMethod }: TApprovalRequestCountDTO) => { + const requestCount = async ({ + projectId, + policyId, + actor, + actorId, + actorOrgId, + actorAuthMethod + }: TApprovalRequestCountDTO) => { if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" }); await permissionService.getProjectPermission({ @@ -176,11 +182,10 @@ export const secretApprovalRequestServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); - const count = await secretApprovalRequestDAL.findProjectRequestCount(projectId, actorId); + const count = await secretApprovalRequestDAL.findProjectRequestCount(projectId, actorId, policyId); return count; }; @@ -204,8 +209,7 @@ export const secretApprovalRequestServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const { shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId); @@ -257,8 +261,7 @@ export const secretApprovalRequestServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); if ( !hasRole(ProjectMembershipRole.Admin) && @@ -407,8 +410,7 @@ export const secretApprovalRequestServiceFactory = ({ actorId, projectId: secretApprovalRequest.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); if ( !hasRole(ProjectMembershipRole.Admin) && @@ -477,8 +479,7 @@ export const secretApprovalRequestServiceFactory = ({ actorId, projectId: secretApprovalRequest.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); if ( !hasRole(ProjectMembershipRole.Admin) && @@ -534,8 +535,7 @@ export const secretApprovalRequestServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); if ( @@ -953,7 +953,7 @@ export const secretApprovalRequestServiceFactory = ({ bypassReason, secretPath: policy.secretPath, environment: env.name, - approvalUrl: `${cfg.SITE_URL}/secret-manager/${project.id}/approval` + approvalUrl: `${cfg.SITE_URL}/projects/${project.id}/secret-manager/approval` }, template: SmtpTemplates.AccessSecretRequestBypassed }); @@ -982,8 +982,7 @@ export const secretApprovalRequestServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); throwIfMissingSecretReadValueOrDescribePermission(permission, ProjectPermissionSecretActions.ReadValue, { @@ -1273,8 +1272,7 @@ export const secretApprovalRequestServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath); if (!folder) diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-types.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-types.ts index 2fdb0bb9d..4d4273d27 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-types.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-types.ts @@ -84,7 +84,7 @@ export type TReviewRequestDTO = { comment?: string; } & Omit; -export type TApprovalRequestCountDTO = TProjectPermission; +export type TApprovalRequestCountDTO = TProjectPermission & { policyId?: string }; export type TListApprovalsDTO = { projectId: string; diff --git a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-queue.ts b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-queue.ts index f15cc4974..38ac137dc 100644 --- a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-queue.ts +++ b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-queue.ts @@ -166,7 +166,9 @@ export const secretRotationV2QueueServiceFactory = async ({ secretPath: folder.path, environment: environment.name, projectName: project.name, - rotationUrl: encodeURI(`${appCfg.SITE_URL}/secret-manager/${projectId}/secrets/${environment.slug}`) + rotationUrl: encodeURI( + `${appCfg.SITE_URL}/projects/${projectId}/secret-manager/secrets/${environment.slug}` + ) } }); } catch (error) { diff --git a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-service.ts b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-service.ts index c1b5b8c25..cd88f889e 100644 --- a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-service.ts +++ b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-service.ts @@ -2,7 +2,7 @@ import { ForbiddenError, subject } from "@casl/ability"; import { Knex } from "knex"; import isEqual from "lodash.isequal"; -import { ActionProjectType, SecretType, TableName } from "@app/db/schemas"; +import { SecretType, TableName } from "@app/db/schemas"; import { EventType, TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-types"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { hasSecretReadValueOrDescribePermission } from "@app/ee/services/permission/permission-fns"; @@ -218,7 +218,7 @@ export const secretRotationV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretManager, + projectId }); @@ -269,7 +269,7 @@ export const secretRotationV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretManager, + projectId }); @@ -315,7 +315,7 @@ export const secretRotationV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretManager, + projectId }); @@ -380,7 +380,7 @@ export const secretRotationV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretManager, + projectId }); @@ -424,7 +424,7 @@ export const secretRotationV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretManager, + projectId }); @@ -625,7 +625,7 @@ export const secretRotationV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretManager, + projectId }); @@ -775,7 +775,7 @@ export const secretRotationV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretManager, + projectId }); @@ -1105,7 +1105,7 @@ export const secretRotationV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretManager, + projectId }); @@ -1152,7 +1152,7 @@ export const secretRotationV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretManager, + projectId }); @@ -1204,7 +1204,7 @@ export const secretRotationV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretManager, + projectId }); @@ -1320,8 +1320,7 @@ export const secretRotationV2ServiceFactory = ({ actorId: actor.id, projectId, actorAuthMethod: actor.authMethod, - actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId: actor.orgId }); const permissiveFolderMappings = folderMappings.filter(({ path, environment }) => diff --git a/backend/src/ee/services/secret-rotation/secret-rotation-service.ts b/backend/src/ee/services/secret-rotation/secret-rotation-service.ts index ee840121a..06aa306d9 100644 --- a/backend/src/ee/services/secret-rotation/secret-rotation-service.ts +++ b/backend/src/ee/services/secret-rotation/secret-rotation-service.ts @@ -1,7 +1,7 @@ import { ForbiddenError, subject } from "@casl/ability"; import Ajv from "ajv"; -import { ActionProjectType, ProjectVersion, TableName } from "@app/db/schemas"; +import { ProjectVersion, TableName } from "@app/db/schemas"; import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography"; import { BadRequestError, NotFoundError } from "@app/lib/errors"; import { TProjectPermission } from "@app/lib/types"; @@ -66,8 +66,7 @@ export const secretRotationServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionSecretRotationActions.Read, @@ -98,8 +97,7 @@ export const secretRotationServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionSecretRotationActions.Read, @@ -215,8 +213,7 @@ export const secretRotationServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionSecretRotationActions.Read, @@ -266,8 +263,7 @@ export const secretRotationServiceFactory = ({ actorId, projectId: project.id, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionSecretRotationActions.Edit, @@ -287,8 +283,7 @@ export const secretRotationServiceFactory = ({ actorId, projectId: doc.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionSecretRotationActions.Delete, diff --git a/backend/src/ee/services/secret-scanning-v2/secret-scanning-v2-queue.ts b/backend/src/ee/services/secret-scanning-v2/secret-scanning-v2-queue.ts index aa8519027..417417c74 100644 --- a/backend/src/ee/services/secret-scanning-v2/secret-scanning-v2-queue.ts +++ b/backend/src/ee/services/secret-scanning-v2/secret-scanning-v2-queue.ts @@ -318,7 +318,7 @@ export const secretScanningV2QueueServiceFactory = async ({ }, { batchSize: 1, - workerCount: 20, + workerCount: 2, pollingIntervalSeconds: 1 } ); @@ -539,7 +539,7 @@ export const secretScanningV2QueueServiceFactory = async ({ }, { batchSize: 1, - workerCount: 20, + workerCount: 2, pollingIntervalSeconds: 1 } ); @@ -588,7 +588,7 @@ export const secretScanningV2QueueServiceFactory = async ({ numberOfSecrets: payload.numberOfSecrets, isDiffScan: payload.isDiffScan, url: encodeURI( - `${appCfg.SITE_URL}/secret-scanning/${projectId}/findings?search=scanId:${payload.scanId}` + `${appCfg.SITE_URL}/projects/${projectId}/secret-scanning/findings?search=scanId:${payload.scanId}` ), timestamp } @@ -599,7 +599,7 @@ export const secretScanningV2QueueServiceFactory = async ({ timestamp, errorMessage: payload.errorMessage, url: encodeURI( - `${appCfg.SITE_URL}/secret-scanning/${projectId}/data-sources/${dataSource.type}/${dataSource.id}` + `${appCfg.SITE_URL}/projects/${projectId}/secret-scanning/data-sources/${dataSource.type}/${dataSource.id}` ) } }); @@ -613,7 +613,7 @@ export const secretScanningV2QueueServiceFactory = async ({ }, { batchSize: 1, - workerCount: 5, + workerCount: 2, pollingIntervalSeconds: 1 } ); diff --git a/backend/src/ee/services/secret-scanning-v2/secret-scanning-v2-service.ts b/backend/src/ee/services/secret-scanning-v2/secret-scanning-v2-service.ts index 34a981116..f1f09506f 100644 --- a/backend/src/ee/services/secret-scanning-v2/secret-scanning-v2-service.ts +++ b/backend/src/ee/services/secret-scanning-v2/secret-scanning-v2-service.ts @@ -1,7 +1,6 @@ import { ForbiddenError } from "@casl/ability"; import { join } from "path"; -import { ActionProjectType } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { @@ -92,7 +91,7 @@ export const secretScanningV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretScanning, + projectId }); @@ -154,7 +153,7 @@ export const secretScanningV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretScanning, + projectId: dataSource.projectId }); @@ -199,7 +198,7 @@ export const secretScanningV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretScanning, + projectId }); @@ -233,7 +232,7 @@ export const secretScanningV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretScanning, + projectId: payload.projectId }); @@ -346,7 +345,7 @@ export const secretScanningV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretScanning, + projectId: dataSource.projectId }); @@ -399,7 +398,7 @@ export const secretScanningV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretScanning, + projectId: dataSource.projectId }); @@ -444,7 +443,7 @@ export const secretScanningV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretScanning, + projectId: dataSource.projectId }); @@ -508,7 +507,7 @@ export const secretScanningV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretScanning, + projectId: dataSource.projectId }); @@ -553,7 +552,7 @@ export const secretScanningV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretScanning, + projectId: dataSource.projectId }); @@ -596,7 +595,7 @@ export const secretScanningV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretScanning, + projectId: dataSource.projectId }); @@ -639,7 +638,7 @@ export const secretScanningV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretScanning, + projectId: dataSource.projectId }); @@ -672,7 +671,7 @@ export const secretScanningV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretScanning, + projectId }); @@ -706,7 +705,7 @@ export const secretScanningV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretScanning, + projectId }); @@ -746,7 +745,7 @@ export const secretScanningV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretScanning, + projectId: finding.projectId }); @@ -777,7 +776,7 @@ export const secretScanningV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretScanning, + projectId }); @@ -812,7 +811,7 @@ export const secretScanningV2ServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretScanning, + projectId }); diff --git a/backend/src/ee/services/secret-snapshot/secret-snapshot-service.ts b/backend/src/ee/services/secret-snapshot/secret-snapshot-service.ts index d8c8c0ead..c6f0c0353 100644 --- a/backend/src/ee/services/secret-snapshot/secret-snapshot-service.ts +++ b/backend/src/ee/services/secret-snapshot/secret-snapshot-service.ts @@ -2,7 +2,7 @@ // akhilmhdh: I did this, quite strange bug with eslint. Everything do have a type stil has this error import { ForbiddenError } from "@casl/ability"; -import { ActionProjectType, TableName, TSecretTagJunctionInsert, TSecretV2TagJunctionInsert } from "@app/db/schemas"; +import { TableName, TSecretTagJunctionInsert, TSecretV2TagJunctionInsert } from "@app/db/schemas"; import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography"; import { InternalServerError, NotFoundError } from "@app/lib/errors"; import { groupBy } from "@app/lib/fn"; @@ -103,8 +103,7 @@ export const secretSnapshotServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback); @@ -140,8 +139,7 @@ export const secretSnapshotServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback); @@ -169,8 +167,7 @@ export const secretSnapshotServiceFactory = ({ actorId, projectId: snapshot.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback); @@ -394,8 +391,7 @@ export const secretSnapshotServiceFactory = ({ actorId, projectId: snapshot.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, diff --git a/backend/src/ee/services/ssh-certificate-template/ssh-certificate-template-service.ts b/backend/src/ee/services/ssh-certificate-template/ssh-certificate-template-service.ts index 49d8c1ab6..e679fdfac 100644 --- a/backend/src/ee/services/ssh-certificate-template/ssh-certificate-template-service.ts +++ b/backend/src/ee/services/ssh-certificate-template/ssh-certificate-template-service.ts @@ -1,6 +1,5 @@ import { ForbiddenError } from "@casl/ability"; -import { ActionProjectType } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { BadRequestError, NotFoundError } from "@app/lib/errors"; @@ -59,8 +58,7 @@ export const sshCertificateTemplateServiceFactory = ({ actorId, projectId: ca.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -132,8 +130,7 @@ export const sshCertificateTemplateServiceFactory = ({ actorId, projectId: certTemplate.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -201,8 +198,7 @@ export const sshCertificateTemplateServiceFactory = ({ actorId, projectId: certificateTemplate.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -228,8 +224,7 @@ export const sshCertificateTemplateServiceFactory = ({ actorId, projectId: certTemplate.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( diff --git a/backend/src/ee/services/ssh-host-group/ssh-host-group-service.ts b/backend/src/ee/services/ssh-host-group/ssh-host-group-service.ts index aa6d4f66a..fba849d93 100644 --- a/backend/src/ee/services/ssh-host-group/ssh-host-group-service.ts +++ b/backend/src/ee/services/ssh-host-group/ssh-host-group-service.ts @@ -1,6 +1,5 @@ import { ForbiddenError } from "@casl/ability"; -import { ActionProjectType } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { TSshHostDALFactory } from "@app/ee/services/ssh-host/ssh-host-dal"; @@ -80,8 +79,7 @@ export const sshHostGroupServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.SshHostGroups); @@ -173,8 +171,7 @@ export const sshHostGroupServiceFactory = ({ actorId, projectId: sshHostGroup.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SshHostGroups); @@ -270,8 +267,7 @@ export const sshHostGroupServiceFactory = ({ actorId, projectId: sshHostGroup.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SshHostGroups); @@ -294,8 +290,7 @@ export const sshHostGroupServiceFactory = ({ actorId, projectId: sshHostGroup.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.SshHostGroups); @@ -321,8 +316,7 @@ export const sshHostGroupServiceFactory = ({ actorId, projectId: sshHostGroup.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SshHostGroups); @@ -360,8 +354,7 @@ export const sshHostGroupServiceFactory = ({ actorId, projectId: sshHostGroup.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SshHostGroups); @@ -400,8 +393,7 @@ export const sshHostGroupServiceFactory = ({ actorId, projectId: sshHostGroup.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SshHostGroups); diff --git a/backend/src/ee/services/ssh-host/ssh-host-fns.ts b/backend/src/ee/services/ssh-host/ssh-host-fns.ts index dec15e093..5b2f98728 100644 --- a/backend/src/ee/services/ssh-host/ssh-host-fns.ts +++ b/backend/src/ee/services/ssh-host/ssh-host-fns.ts @@ -1,6 +1,5 @@ import { Knex } from "knex"; -import { ActionProjectType } from "@app/db/schemas"; import { BadRequestError } from "@app/lib/errors"; import { ProjectPermissionSshHostActions, ProjectPermissionSub } from "../permission/project-permission"; @@ -63,8 +62,7 @@ export const createSshLoginMappings = async ({ userId: user.id, projectId, authMethod: actorAuthMethod, - userOrgId: actorOrgId, - actionProjectType: ActionProjectType.SSH + userOrgId: actorOrgId }); } diff --git a/backend/src/ee/services/ssh-host/ssh-host-service.ts b/backend/src/ee/services/ssh-host/ssh-host-service.ts index 64abfebbc..36bc1bbb3 100644 --- a/backend/src/ee/services/ssh-host/ssh-host-service.ts +++ b/backend/src/ee/services/ssh-host/ssh-host-service.ts @@ -1,6 +1,5 @@ import { ForbiddenError, subject } from "@casl/ability"; -import { ActionProjectType, ProjectType } from "@app/db/schemas"; import { TGroupDALFactory } from "@app/ee/services/group/group-dal"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionSshHostActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; @@ -12,11 +11,13 @@ import { SshCertKeyAlgorithm } from "@app/ee/services/ssh-certificate/ssh-certif import { TSshHostDALFactory } from "@app/ee/services/ssh-host/ssh-host-dal"; import { TSshHostLoginUserMappingDALFactory } from "@app/ee/services/ssh-host/ssh-host-login-user-mapping-dal"; import { TSshHostLoginUserDALFactory } from "@app/ee/services/ssh-host/ssh-login-user-dal"; +import { PgSqlLock } from "@app/keystore/keystore"; import { BadRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors"; import { ActorType } from "@app/services/auth/auth-type"; import { TKmsServiceFactory } from "@app/services/kms/kms-service"; import { KmsDataKey } from "@app/services/kms/kms-types"; import { TProjectDALFactory } from "@app/services/project/project-dal"; +import { bootstrapSshProject } from "@app/services/project/project-fns"; import { TProjectSshConfigDALFactory } from "@app/services/project/project-ssh-config-dal"; import { TUserDALFactory } from "@app/services/user/user-dal"; @@ -43,9 +44,9 @@ type TSshHostServiceFactoryDep = { userDAL: Pick; groupDAL: Pick; projectDAL: Pick; - projectSshConfigDAL: Pick; - sshCertificateAuthorityDAL: Pick; - sshCertificateAuthoritySecretDAL: Pick; + projectSshConfigDAL: Pick; + sshCertificateAuthorityDAL: Pick; + sshCertificateAuthoritySecretDAL: Pick; sshCertificateDAL: Pick; sshCertificateBodyDAL: Pick; userGroupMembershipDAL: Pick; @@ -98,8 +99,7 @@ export const sshHostServiceFactory = ({ } const sshProjects = await projectDAL.find({ - orgId: actorOrgId, - type: ProjectType.SSH + orgId: actorOrgId }); const allowedHosts = []; @@ -111,8 +111,7 @@ export const sshHostServiceFactory = ({ actorId, projectId: project.id, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); const projectHosts = await sshHostDAL.findUserAccessibleSshHosts([project.id], actorId); @@ -145,8 +144,7 @@ export const sshHostServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -184,7 +182,25 @@ export const sshHostServiceFactory = ({ return ca.id; }; - const projectSshConfig = await projectSshConfigDAL.findOne({ projectId }); + let projectSshConfig = await projectSshConfigDAL.findOne({ projectId }); + if (!projectSshConfig) { + projectSshConfig = await projectSshConfigDAL.transaction(async (tx) => { + await tx.raw("SELECT pg_advisory_xact_lock(?)", [PgSqlLock.SshInit(projectId)]); + + let sshConfig = await projectSshConfigDAL.findOne({ projectId }, tx); + if (sshConfig) return sshConfig; + + sshConfig = await bootstrapSshProject({ + projectId, + sshCertificateAuthorityDAL, + sshCertificateAuthoritySecretDAL, + kmsService, + projectSshConfigDAL, + tx + }); + return sshConfig; + }); + } const userSshCaId = await resolveSshCaId({ requestedId: requestedUserSshCaId, @@ -257,8 +273,7 @@ export const sshHostServiceFactory = ({ actorId, projectId: host.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -319,8 +334,7 @@ export const sshHostServiceFactory = ({ actorId, projectId: host.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -348,8 +362,7 @@ export const sshHostServiceFactory = ({ actorId, projectId: host.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -388,8 +401,7 @@ export const sshHostServiceFactory = ({ actorId, projectId: host.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); const internalPrincipals = await convertActorToPrincipals({ @@ -508,8 +520,7 @@ export const sshHostServiceFactory = ({ actorId, projectId: host.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( diff --git a/backend/src/ee/services/ssh/ssh-certificate-authority-service.ts b/backend/src/ee/services/ssh/ssh-certificate-authority-service.ts index 6c35f0ddd..2e45c836d 100644 --- a/backend/src/ee/services/ssh/ssh-certificate-authority-service.ts +++ b/backend/src/ee/services/ssh/ssh-certificate-authority-service.ts @@ -1,6 +1,5 @@ import { ForbiddenError } from "@casl/ability"; -import { ActionProjectType } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { TSshCertificateAuthorityDALFactory } from "@app/ee/services/ssh/ssh-certificate-authority-dal"; @@ -73,8 +72,7 @@ export const sshCertificateAuthorityServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -109,8 +107,7 @@ export const sshCertificateAuthorityServiceFactory = ({ actorId, projectId: ca.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -178,8 +175,7 @@ export const sshCertificateAuthorityServiceFactory = ({ actorId, projectId: ca.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -217,8 +213,7 @@ export const sshCertificateAuthorityServiceFactory = ({ actorId, projectId: ca.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -259,8 +254,7 @@ export const sshCertificateAuthorityServiceFactory = ({ actorId, projectId: sshCertificateTemplate.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -381,8 +375,7 @@ export const sshCertificateAuthorityServiceFactory = ({ actorId, projectId: sshCertificateTemplate.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -479,8 +472,7 @@ export const sshCertificateAuthorityServiceFactory = ({ actorId, projectId: ca.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( diff --git a/backend/src/ee/services/trusted-ip/trusted-ip-service.ts b/backend/src/ee/services/trusted-ip/trusted-ip-service.ts index 6b9686e25..69e7e5e1d 100644 --- a/backend/src/ee/services/trusted-ip/trusted-ip-service.ts +++ b/backend/src/ee/services/trusted-ip/trusted-ip-service.ts @@ -1,6 +1,5 @@ import { ForbiddenError } from "@casl/ability"; -import { ActionProjectType } from "@app/db/schemas"; import { BadRequestError } from "@app/lib/errors"; import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip"; import { TProjectDALFactory } from "@app/services/project/project-dal"; @@ -36,8 +35,7 @@ export const trustedIpServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList); const trustedIps = await trustedIpDAL.find({ @@ -61,8 +59,7 @@ export const trustedIpServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList); @@ -107,8 +104,7 @@ export const trustedIpServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList); @@ -153,8 +149,7 @@ export const trustedIpServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList); diff --git a/backend/src/keystore/keystore.ts b/backend/src/keystore/keystore.ts index 0aa35b73d..79679d256 100644 --- a/backend/src/keystore/keystore.ts +++ b/backend/src/keystore/keystore.ts @@ -12,7 +12,8 @@ export const PgSqlLock = { OrgGatewayCertExchange: (orgId: string) => pgAdvisoryLockHashText(`org-gateway-cert-exchange:${orgId}`), SecretRotationV2Creation: (folderId: string) => pgAdvisoryLockHashText(`secret-rotation-v2-creation:${folderId}`), CreateProject: (orgId: string) => pgAdvisoryLockHashText(`create-project:${orgId}`), - CreateFolder: (envId: string, projectId: string) => pgAdvisoryLockHashText(`create-folder:${envId}-${projectId}`) + CreateFolder: (envId: string, projectId: string) => pgAdvisoryLockHashText(`create-folder:${envId}-${projectId}`), + SshInit: (projectId: string) => pgAdvisoryLockHashText(`ssh-bootstrap:${projectId}`) } as const; // all the key prefixes used must be set here to avoid conflict diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index 1e169f58d..f3d73896e 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -66,7 +66,10 @@ export enum ApiDocsTags { KmsKeys = "KMS Keys", KmsEncryption = "KMS Encryption", KmsSigning = "KMS Signing", - SecretScanning = "Secret Scanning" + SecretScanning = "Secret Scanning", + OidcSso = "OIDC SSO", + SamlSso = "SAML SSO", + LdapSso = "LDAP SSO" } export const GROUPS = { @@ -700,7 +703,8 @@ export const PROJECTS = { slug: "An optional slug for the project. (must be unique within the organization)", hasDeleteProtection: "Enable or disable delete protection for the project.", secretSharing: "Enable or disable secret sharing for the project.", - showSnapshotsLegacy: "Enable or disable legacy snapshots for the project." + showSnapshotsLegacy: "Enable or disable legacy snapshots for the project.", + defaultProduct: "The default product in which the project will open" }, GET_KEY: { workspaceId: "The ID of the project to get the key from." @@ -2267,6 +2271,10 @@ export const AppConnections = { accessToken: "The Access Token used to access GitLab.", code: "The OAuth code to use to connect with GitLab.", accessTokenType: "The type of token used to connect with GitLab." + }, + ZABBIX: { + apiToken: "The API Token used to access Zabbix.", + instanceUrl: "The Zabbix instance URL to connect with." } } }; @@ -2456,6 +2464,12 @@ export const SecretSyncs = { CLOUDFLARE_PAGES: { projectName: "The name of the Cloudflare Pages project to sync secrets to.", environment: "The environment of the Cloudflare Pages project to sync secrets to." + }, + ZABBIX: { + scope: "The Zabbix scope that secrets should be synced to.", + hostId: "The ID of the Zabbix host to sync secrets to.", + hostName: "The name of the Zabbix host to sync secrets to.", + macroType: "The type of macro to sync secrets to. (0: Text, 1: Secret)" } } }; @@ -2651,3 +2665,113 @@ export const SecretScanningConfigs = { content: "The contents of the Secret Scanning Configuration file." } }; + +export const OidcSSo = { + GET_CONFIG: { + organizationId: "The ID of the organization to get the OIDC config for." + }, + UPDATE_CONFIG: { + organizationId: "The ID of the organization to update the OIDC config for.", + allowedEmailDomains: + "A list of allowed email domains that users can use to authenticate with. This field is comma separated. Example: 'example.com,acme.com'", + discoveryURL: "The URL of the OIDC discovery endpoint.", + configurationType: "The configuration type to use for the OIDC configuration.", + issuer: + "The issuer for the OIDC configuration. This is only supported when the OIDC configuration type is set to 'custom'.", + authorizationEndpoint: + "The endpoint to use for OIDC authorization. This is only supported when the OIDC configuration type is set to 'custom'.", + jwksUri: "The URL of the OIDC JWKS endpoint.", + tokenEndpoint: "The token endpoint to use for OIDC token exchange.", + userinfoEndpoint: "The userinfo endpoint to get user information from the OIDC provider.", + clientId: "The client ID to use for OIDC authentication.", + clientSecret: "The client secret to use for OIDC authentication.", + isActive: "Whether to enable or disable this OIDC configuration.", + manageGroupMemberships: + "Whether to manage group memberships for the OIDC configuration. If enabled, users will automatically be assigned groups when they sign in, based on which groups they are a member of in the OIDC provider.", + jwtSignatureAlgorithm: "The algorithm to use for JWT signature verification." + }, + CREATE_CONFIG: { + organizationId: "The ID of the organization to create the OIDC config for.", + allowedEmailDomains: + "A list of allowed email domains that users can use to authenticate with. This field is comma separated.", + discoveryURL: "The URL of the OIDC discovery endpoint.", + configurationType: "The configuration type to use for the OIDC configuration.", + issuer: + "The issuer for the OIDC configuration. This is only supported when the OIDC configuration type is set to 'custom'.", + authorizationEndpoint: + "The authorization endpoint to use for OIDC authorization. This is only supported when the OIDC configuration type is set to 'custom'.", + jwksUri: "The URL of the OIDC JWKS endpoint.", + tokenEndpoint: "The token endpoint to use for OIDC token exchange.", + userinfoEndpoint: "The userinfo endpoint to get user information from the OIDC provider.", + clientId: "The client ID to use for OIDC authentication.", + clientSecret: "The client secret to use for OIDC authentication.", + isActive: "Whether to enable or disable this OIDC configuration.", + manageGroupMemberships: + "Whether to manage group memberships for the OIDC configuration. If enabled, users will automatically be assigned groups when they sign in, based on which groups they are a member of in the OIDC provider.", + jwtSignatureAlgorithm: "The algorithm to use for JWT signature verification." + } +}; + +export const SamlSso = { + GET_CONFIG: { + organizationId: "The ID of the organization to get the SAML config for." + }, + UPDATE_CONFIG: { + organizationId: "The ID of the organization to update the SAML config for.", + authProvider: "Authentication provider to use for SAML authentication.", + isActive: "Whether to enable or disable this SAML configuration.", + entryPoint: + "The entry point for the SAML authentication. This is the URL that the user will be redirected to after they have authenticated with the SAML provider.", + issuer: "The SAML provider issuer URL or entity ID.", + cert: "The certificate to use for SAML authentication." + }, + CREATE_CONFIG: { + organizationId: "The ID of the organization to create the SAML config for.", + authProvider: "Authentication provider to use for SAML authentication.", + isActive: "Whether to enable or disable this SAML configuration.", + entryPoint: + "The entry point for the SAML authentication. This is the URL that the user will be redirected to after they have authenticated with the SAML provider.", + issuer: "The SAML provider issuer URL or entity ID.", + cert: "The certificate to use for SAML authentication." + } +}; + +export const LdapSso = { + GET_CONFIG: { + organizationId: "The ID of the organization to get the LDAP config for." + }, + CREATE_CONFIG: { + organizationId: "The ID of the organization to create the LDAP config for.", + isActive: "Whether to enable or disable this LDAP configuration.", + url: "The LDAP server to connect to such as `ldap://ldap.your-org.com`, `ldaps://ldap.myorg.com:636` (for connection over SSL/TLS), etc.", + bindDN: + "The distinguished name of the object to bind when performing the user search such as `cn=infisical,ou=Users,dc=acme,dc=com`", + bindPass: "The password to use along with Bind DN when performing the user search.", + searchBase: "The base DN to use for the user search such as `ou=Users,dc=acme,dc=com`", + uniqueUserAttribute: + "The attribute to use as the unique identifier of LDAP users such as `sAMAccountName`, `cn`, `uid`, `objectGUID`. If left blank, defaults to uidNumber", + searchFilter: + "The template used to construct the LDAP user search filter such as `(uid={{username}})` uses literal `{{username}}` to have the given username used in the search. The default is `(uid={{username}})` which is compatible with several common directory schemas.", + groupSearchBase: "LDAP search base to use for group membership search such as `ou=Groups,dc=acme,dc=com`", + groupSearchFilter: + "The template used when constructing the group membership query such as `(&(objectClass=posixGroup)(memberUid={{.Username}}))`. The template can access the following context variables: `[UserDN, UserName]`. The default is `(|(memberUid={{.Username}})(member={{.UserDN}})(uniqueMember={{.UserDN}}))` which is compatible with several common directory schemas.", + caCert: "The CA certificate to use when verifying the LDAP server certificate." + }, + UPDATE_CONFIG: { + organizationId: "The ID of the organization to update the LDAP config for.", + isActive: "Whether to enable or disable this LDAP configuration.", + url: "The LDAP server to connect to such as `ldap://ldap.your-org.com`, `ldaps://ldap.myorg.com:636` (for connection over SSL/TLS), etc.", + bindDN: + "The distinguished name of object to bind when performing the user search such as `cn=infisical,ou=Users,dc=acme,dc=com`", + bindPass: "The password to use along with Bind DN when performing the user search.", + uniqueUserAttribute: + "The attribute to use as the unique identifier of LDAP users such as `sAMAccountName`, `cn`, `uid`, `objectGUID`. If left blank, defaults to uidNumber", + searchFilter: + "The template used to construct the LDAP user search filter such as `(uid={{username}})` uses literal `{{username}}` to have the given username used in the search. The default is `(uid={{username}})` which is compatible with several common directory schemas.", + searchBase: "The base DN to use for the user search such as `ou=Users,dc=acme,dc=com`", + groupSearchBase: "LDAP search base to use for group membership search such as `ou=Groups,dc=acme,dc=com`", + groupSearchFilter: + "The template used when constructing the group membership query such as `(&(objectClass=posixGroup)(memberUid={{.Username}}))`. The template can access the following context variables: `[UserDN, UserName]`. The default is `(|(memberUid={{.Username}})(member={{.UserDN}})(uniqueMember={{.UserDN}}))` which is compatible with several common directory schemas.", + caCert: "The CA certificate to use when verifying the LDAP server certificate." + } +}; diff --git a/backend/src/lib/config/env.ts b/backend/src/lib/config/env.ts index 818b3e254..44ca5363f 100644 --- a/backend/src/lib/config/env.ts +++ b/backend/src/lib/config/env.ts @@ -2,6 +2,7 @@ import { z } from "zod"; import { QueueWorkerProfile } from "@app/lib/types"; +import { BadRequestError } from "../errors"; import { removeTrailingSlash } from "../fn"; import { CustomLogger } from "../logger/logger"; import { zpStr } from "../zod"; @@ -342,8 +343,11 @@ const envSchema = z export type TEnvConfig = Readonly>; let envCfg: TEnvConfig; +let originalEnvConfig: TEnvConfig; export const getConfig = () => envCfg; +export const getOriginalConfig = () => originalEnvConfig; + // cannot import singleton logger directly as it needs config to load various transport export const initEnvConfig = (logger?: CustomLogger) => { const parsedEnv = envSchema.safeParse(process.env); @@ -353,23 +357,113 @@ export const initEnvConfig = (logger?: CustomLogger) => { process.exit(-1); } - const updateRootEncryptionKey = (key?: string) => { - if (!key) { - throw new Error("Failed to update root encryption key. Key is unset."); - } + const config = Object.freeze(parsedEnv.data); + envCfg = config; - const newEnvCfg = { - ...envCfg - }; + if (!originalEnvConfig) { + originalEnvConfig = config; + } - newEnvCfg.ROOT_ENCRYPTION_KEY = key; - delete newEnvCfg.ENCRYPTION_KEY; + return envCfg; +}; - envCfg = Object.freeze(newEnvCfg); - return envCfg; +// A list of environment variables that can be overwritten +export const overwriteSchema: { + [key: string]: { + name: string; + fields: { key: keyof TEnvConfig; description?: string }[]; }; +} = { + azure: { + name: "Azure", + fields: [ + { + key: "INF_APP_CONNECTION_AZURE_CLIENT_ID", + description: "The Application (Client) ID of your Azure application." + }, + { + key: "INF_APP_CONNECTION_AZURE_CLIENT_SECRET", + description: "The Client Secret of your Azure application." + } + ] + }, + google_sso: { + name: "Google SSO", + fields: [ + { + key: "CLIENT_ID_GOOGLE_LOGIN", + description: "The Client ID of your GCP OAuth2 application." + }, + { + key: "CLIENT_SECRET_GOOGLE_LOGIN", + description: "The Client Secret of your GCP OAuth2 application." + } + ] + }, + github_sso: { + name: "GitHub SSO", + fields: [ + { + key: "CLIENT_ID_GITHUB_LOGIN", + description: "The Client ID of your GitHub OAuth application." + }, + { + key: "CLIENT_SECRET_GITHUB_LOGIN", + description: "The Client Secret of your GitHub OAuth application." + } + ] + }, + gitlab_sso: { + name: "GitLab SSO", + fields: [ + { + key: "CLIENT_ID_GITLAB_LOGIN", + description: "The Client ID of your GitLab application." + }, + { + key: "CLIENT_SECRET_GITLAB_LOGIN", + description: "The Secret of your GitLab application." + }, + { + key: "CLIENT_GITLAB_LOGIN_URL", + description: + "The URL of your self-hosted instance of GitLab where the OAuth application is registered. If no URL is passed in, this will default to https://gitlab.com." + } + ] + } +}; - return { envCfg, updateRootEncryptionKey }; +export const overridableKeys = new Set( + Object.values(overwriteSchema).flatMap(({ fields }) => fields.map(({ key }) => key)) +); + +export const validateOverrides = (config: Record) => { + const allowedOverrides = Object.fromEntries( + Object.entries(config).filter(([key]) => overridableKeys.has(key as keyof z.input)) + ); + + const tempEnv: Record = { ...process.env, ...allowedOverrides }; + const parsedResult = envSchema.safeParse(tempEnv); + + if (!parsedResult.success) { + const errorDetails = parsedResult.error.issues + .map((issue) => `Key: "${issue.path.join(".")}", Error: ${issue.message}`) + .join("\n"); + throw new BadRequestError({ message: errorDetails }); + } +}; + +export const overrideEnvConfig = (config: Record) => { + const allowedOverrides = Object.fromEntries( + Object.entries(config).filter(([key]) => overridableKeys.has(key as keyof z.input)) + ); + + const tempEnv: Record = { ...process.env, ...allowedOverrides }; + const parsedResult = envSchema.safeParse(tempEnv); + + if (parsedResult.success) { + envCfg = Object.freeze(parsedResult.data); + } }; export const formatSmtpConfig = () => { diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index 8aeeffdd3..db7f0954b 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -300,6 +300,7 @@ import { injectIdentity } from "../plugins/auth/inject-identity"; import { injectPermission } from "../plugins/auth/inject-permission"; import { injectRateLimits } from "../plugins/inject-rate-limits"; import { registerV1Routes } from "./v1"; +import { initializeOauthConfigSync } from "./v1/sso-router"; import { registerV2Routes } from "./v2"; import { registerV3Routes } from "./v3"; @@ -996,8 +997,7 @@ export const registerRoutes = async ( pkiAlertDAL, pkiCollectionDAL, permissionService, - smtpService, - projectDAL + smtpService }); const pkiCollectionService = pkiCollectionServiceFactory({ @@ -1005,8 +1005,7 @@ export const registerRoutes = async ( pkiCollectionItemDAL, certificateAuthorityDAL, certificateDAL, - permissionService, - projectDAL + permissionService }); const projectTemplateService = projectTemplateServiceFactory({ @@ -1190,7 +1189,9 @@ export const registerRoutes = async ( projectEnvDAL, snapshotService, projectDAL, - folderCommitService + folderCommitService, + secretApprovalPolicyService, + secretV2BridgeDAL }); const secretImportService = secretImportServiceFactory({ @@ -1616,7 +1617,8 @@ export const registerRoutes = async ( secretSharingDAL, secretVersionV2DAL: secretVersionV2BridgeDAL, identityUniversalAuthClientSecretDAL: identityUaClientSecretDAL, - serviceTokenService + serviceTokenService, + orgService }); const dailyExpiringPkiItemAlert = dailyExpiringPkiItemAlertQueueServiceFactory({ @@ -1664,8 +1666,7 @@ export const registerRoutes = async ( const cmekService = cmekServiceFactory({ kmsDAL, kmsService, - permissionService, - projectDAL + permissionService }); const externalMigrationQueue = externalMigrationQueueFactory({ @@ -1807,7 +1808,6 @@ export const registerRoutes = async ( const certificateAuthorityService = certificateAuthorityServiceFactory({ certificateAuthorityDAL, - projectDAL, permissionService, appConnectionDAL, appConnectionService, @@ -1817,7 +1817,8 @@ export const registerRoutes = async ( certificateBodyDAL, certificateSecretDAL, kmsService, - pkiSubscriberDAL + pkiSubscriberDAL, + projectDAL }); const internalCaFns = InternalCertificateAuthorityFns({ @@ -2047,6 +2048,16 @@ export const registerRoutes = async ( } } + const configSyncJob = await superAdminService.initializeEnvConfigSync(); + if (configSyncJob) { + cronJobs.push(configSyncJob); + } + + const oauthConfigSyncJob = await initializeOauthConfigSync(); + if (oauthConfigSyncJob) { + cronJobs.push(oauthConfigSyncJob); + } + server.decorate("store", { user: userDAL, kmipClient: kmipClientDAL diff --git a/backend/src/server/routes/sanitizedSchemas.ts b/backend/src/server/routes/sanitizedSchemas.ts index ce51b1079..beef663b9 100644 --- a/backend/src/server/routes/sanitizedSchemas.ts +++ b/backend/src/server/routes/sanitizedSchemas.ts @@ -251,6 +251,7 @@ export const SanitizedProjectSchema = ProjectsSchema.pick({ name: true, description: true, type: true, + defaultProduct: true, slug: true, autoCapitalization: true, orgId: true, diff --git a/backend/src/server/routes/v1/admin-router.ts b/backend/src/server/routes/v1/admin-router.ts index 8715a348f..e57c2a58e 100644 --- a/backend/src/server/routes/v1/admin-router.ts +++ b/backend/src/server/routes/v1/admin-router.ts @@ -8,7 +8,7 @@ import { SuperAdminSchema, UsersSchema } from "@app/db/schemas"; -import { getConfig } from "@app/lib/config/env"; +import { getConfig, overridableKeys } from "@app/lib/config/env"; import { crypto } from "@app/lib/crypto/cryptography"; import { BadRequestError } from "@app/lib/errors"; import { invalidateCacheLimit, readLimit, writeLimit } from "@app/server/config/rateLimiter"; @@ -43,7 +43,8 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => { encryptedGitHubAppConnectionClientSecret: true, encryptedGitHubAppConnectionSlug: true, encryptedGitHubAppConnectionId: true, - encryptedGitHubAppConnectionPrivateKey: true + encryptedGitHubAppConnectionPrivateKey: true, + encryptedEnvOverrides: true }).extend({ isMigrationModeOn: z.boolean(), defaultAuthOrgSlug: z.string().nullable(), @@ -113,11 +114,14 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => { .refine((content) => DOMPurify.sanitize(content) === content, { message: "Page frame content contains unsafe HTML." }) - .optional() + .optional(), + envOverrides: z.record(z.enum(Array.from(overridableKeys) as [string, ...string[]]), z.string()).optional() }), response: { 200: z.object({ - config: SuperAdminSchema.extend({ + config: SuperAdminSchema.omit({ + encryptedEnvOverrides: true + }).extend({ defaultAuthOrgSlug: z.string().nullable() }) }) @@ -384,6 +388,41 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => { } }); + server.route({ + method: "GET", + url: "/env-overrides", + config: { + rateLimit: readLimit + }, + schema: { + response: { + 200: z.record( + z.string(), + z.object({ + name: z.string(), + fields: z + .object({ + key: z.string(), + value: z.string(), + hasEnvEntry: z.boolean(), + description: z.string().optional() + }) + .array() + }) + ) + } + }, + onRequest: (req, res, done) => { + verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => { + verifySuperAdmin(req, res, done); + }); + }, + handler: async () => { + const envOverrides = await server.services.superAdmin.getEnvOverridesOrganized(); + return envOverrides; + } + }); + server.route({ method: "DELETE", url: "/user-management/users/:userId", diff --git a/backend/src/server/routes/v1/app-connection-routers/app-connection-router.ts b/backend/src/server/routes/v1/app-connection-routers/app-connection-router.ts index 6160828f4..032dd939e 100644 --- a/backend/src/server/routes/v1/app-connection-routers/app-connection-router.ts +++ b/backend/src/server/routes/v1/app-connection-routers/app-connection-router.ts @@ -84,6 +84,7 @@ import { SanitizedWindmillConnectionSchema, WindmillConnectionListItemSchema } from "@app/services/app-connection/windmill"; +import { SanitizedZabbixConnectionSchema, ZabbixConnectionListItemSchema } from "@app/services/app-connection/zabbix"; import { AuthMode } from "@app/services/auth/auth-type"; // can't use discriminated due to multiple schemas for certain apps @@ -116,7 +117,8 @@ const SanitizedAppConnectionSchema = z.union([ ...SanitizedRenderConnectionSchema.options, ...SanitizedFlyioConnectionSchema.options, ...SanitizedGitLabConnectionSchema.options, - ...SanitizedCloudflareConnectionSchema.options + ...SanitizedCloudflareConnectionSchema.options, + ...SanitizedZabbixConnectionSchema.options ]); const AppConnectionOptionsSchema = z.discriminatedUnion("app", [ @@ -148,7 +150,8 @@ const AppConnectionOptionsSchema = z.discriminatedUnion("app", [ RenderConnectionListItemSchema, FlyioConnectionListItemSchema, GitLabConnectionListItemSchema, - CloudflareConnectionListItemSchema + CloudflareConnectionListItemSchema, + ZabbixConnectionListItemSchema ]); export const registerAppConnectionRouter = async (server: FastifyZodProvider) => { diff --git a/backend/src/server/routes/v1/app-connection-routers/index.ts b/backend/src/server/routes/v1/app-connection-routers/index.ts index cd4ccd728..35958ddf8 100644 --- a/backend/src/server/routes/v1/app-connection-routers/index.ts +++ b/backend/src/server/routes/v1/app-connection-routers/index.ts @@ -29,6 +29,7 @@ import { registerTeamCityConnectionRouter } from "./teamcity-connection-router"; import { registerTerraformCloudConnectionRouter } from "./terraform-cloud-router"; import { registerVercelConnectionRouter } from "./vercel-connection-router"; import { registerWindmillConnectionRouter } from "./windmill-connection-router"; +import { registerZabbixConnectionRouter } from "./zabbix-connection-router"; export * from "./app-connection-router"; @@ -62,5 +63,6 @@ export const APP_CONNECTION_REGISTER_ROUTER_MAP: Record { + registerAppConnectionEndpoints({ + app: AppConnection.Zabbix, + server, + sanitizedResponseSchema: SanitizedZabbixConnectionSchema, + createSchema: CreateZabbixConnectionSchema, + updateSchema: UpdateZabbixConnectionSchema + }); + + // The following endpoints are for internal Infisical App use only and not part of the public API + server.route({ + method: "GET", + url: `/:connectionId/hosts`, + config: { + rateLimit: readLimit + }, + schema: { + params: z.object({ + connectionId: z.string().uuid() + }), + response: { + 200: z + .object({ + hostId: z.string(), + host: z.string() + }) + .array() + } + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: async (req) => { + const { connectionId } = req.params; + const hosts = await server.services.appConnection.zabbix.listHosts(connectionId, req.permission); + return hosts; + } + }); +}; diff --git a/backend/src/server/routes/v1/organization-router.ts b/backend/src/server/routes/v1/organization-router.ts index b3fceb201..e1669c784 100644 --- a/backend/src/server/routes/v1/organization-router.ts +++ b/backend/src/server/routes/v1/organization-router.ts @@ -113,52 +113,73 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { hide: false, tags: [ApiDocsTags.AuditLogs], description: "Get all audit logs for an organization", - querystring: z.object({ - projectId: z.string().optional().describe(AUDIT_LOGS.EXPORT.projectId), - environment: z.string().optional().describe(AUDIT_LOGS.EXPORT.environment), - actorType: z.nativeEnum(ActorType).optional(), - secretPath: z - .string() - .optional() - .transform((val) => (!val ? val : removeTrailingSlash(val))) - .describe(AUDIT_LOGS.EXPORT.secretPath), - secretKey: z.string().optional().describe(AUDIT_LOGS.EXPORT.secretKey), + querystring: z + .object({ + projectId: z.string().optional().describe(AUDIT_LOGS.EXPORT.projectId), + environment: z.string().optional().describe(AUDIT_LOGS.EXPORT.environment), + actorType: z.nativeEnum(ActorType).optional(), + secretPath: z + .string() + .optional() + .transform((val) => (!val ? val : removeTrailingSlash(val))) + .describe(AUDIT_LOGS.EXPORT.secretPath), + secretKey: z.string().optional().describe(AUDIT_LOGS.EXPORT.secretKey), + // eventType is split with , for multiple values, we need to transform it to array + eventType: z + .string() + .optional() + .transform((val) => (val ? val.split(",") : undefined)), + userAgentType: z.nativeEnum(UserAgentType).optional().describe(AUDIT_LOGS.EXPORT.userAgentType), + eventMetadata: z + .string() + .optional() + .transform((val) => { + if (!val) { + return undefined; + } - // eventType is split with , for multiple values, we need to transform it to array - eventType: z - .string() - .optional() - .transform((val) => (val ? val.split(",") : undefined)), - userAgentType: z.nativeEnum(UserAgentType).optional().describe(AUDIT_LOGS.EXPORT.userAgentType), - eventMetadata: z - .string() - .optional() - .transform((val) => { - if (!val) { - return undefined; + const pairs = val.split(","); + + return pairs.reduce( + (acc, pair) => { + const [key, value] = pair.split("="); + if (key && value) { + acc[key] = value; + } + return acc; + }, + {} as Record + ); + }) + .describe(AUDIT_LOGS.EXPORT.eventMetadata), + startDate: z.string().datetime().optional().describe(AUDIT_LOGS.EXPORT.startDate), + endDate: z.string().datetime().optional().describe(AUDIT_LOGS.EXPORT.endDate), + offset: z.coerce.number().default(0).describe(AUDIT_LOGS.EXPORT.offset), + limit: z.coerce.number().max(1000).default(20).describe(AUDIT_LOGS.EXPORT.limit), + actor: z.string().optional().describe(AUDIT_LOGS.EXPORT.actor) + }) + .superRefine((el, ctx) => { + if (el.endDate && el.startDate) { + const startDate = new Date(el.startDate); + const endDate = new Date(el.endDate); + const maxAllowedDate = new Date(startDate); + maxAllowedDate.setMonth(maxAllowedDate.getMonth() + 3); + if (endDate < startDate) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: ["endDate"], + message: "End date cannot be before start date" + }); } - - const pairs = val.split(","); - - return pairs.reduce( - (acc, pair) => { - const [key, value] = pair.split("="); - if (key && value) { - acc[key] = value; - } - return acc; - }, - {} as Record - ); - }) - .describe(AUDIT_LOGS.EXPORT.eventMetadata), - startDate: z.string().datetime().optional().describe(AUDIT_LOGS.EXPORT.startDate), - endDate: z.string().datetime().optional().describe(AUDIT_LOGS.EXPORT.endDate), - offset: z.coerce.number().default(0).describe(AUDIT_LOGS.EXPORT.offset), - limit: z.coerce.number().default(20).describe(AUDIT_LOGS.EXPORT.limit), - actor: z.string().optional().describe(AUDIT_LOGS.EXPORT.actor) - }), - + if (endDate > maxAllowedDate) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: ["endDate"], + message: "Dates must be within 3 months" + }); + } + } + }), response: { 200: z.object({ auditLogs: AuditLogsSchema.omit({ @@ -188,14 +209,13 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { const auditLogs = await server.services.auditLog.listAuditLogs({ filter: { ...req.query, - endDate: req.query.endDate, + endDate: req.query.endDate || new Date().toISOString(), projectId: req.query.projectId, startDate: req.query.startDate || getLastMidnightDateISO(), auditLogActorId: req.query.actor, actorType: req.query.actorType, eventType: req.query.eventType as EventType[] | undefined }, - actorId: req.permission.id, actorOrgId: req.permission.orgId, actorAuthMethod: req.permission.authMethod, diff --git a/backend/src/server/routes/v1/project-router.ts b/backend/src/server/routes/v1/project-router.ts index cc94adede..2015842a5 100644 --- a/backend/src/server/routes/v1/project-router.ts +++ b/backend/src/server/routes/v1/project-router.ts @@ -158,17 +158,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { includeRoles: z .enum(["true", "false"]) .default("false") - .transform((value) => value === "true"), - type: z - .enum([ - ProjectType.SecretManager, - ProjectType.KMS, - ProjectType.CertificateManager, - ProjectType.SSH, - ProjectType.SecretScanning, - "all" - ]) - .optional() + .transform((value) => value === "true") }), response: { 200: z.object({ @@ -187,8 +177,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { actorId: req.permission.id, actorAuthMethod: req.permission.authMethod, actor: req.permission.type, - actorOrgId: req.permission.orgId, - type: req.query.type + actorOrgId: req.permission.orgId }); return { workspaces }; } @@ -377,7 +366,8 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { .optional() .describe(PROJECTS.UPDATE.slug), secretSharing: z.boolean().optional().describe(PROJECTS.UPDATE.secretSharing), - showSnapshotsLegacy: z.boolean().optional().describe(PROJECTS.UPDATE.showSnapshotsLegacy) + showSnapshotsLegacy: z.boolean().optional().describe(PROJECTS.UPDATE.showSnapshotsLegacy), + defaultProduct: z.nativeEnum(ProjectType).optional().describe(PROJECTS.UPDATE.defaultProduct) }), response: { 200: z.object({ @@ -396,6 +386,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { name: req.body.name, description: req.body.description, autoCapitalization: req.body.autoCapitalization, + defaultProduct: req.body.defaultProduct, hasDeleteProtection: req.body.hasDeleteProtection, slug: req.body.slug, secretSharing: req.body.secretSharing, @@ -1059,7 +1050,6 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { body: z.object({ limit: z.number().default(100), offset: z.number().default(0), - type: z.nativeEnum(ProjectType).optional(), orderBy: z.nativeEnum(SearchProjectSortBy).optional().default(SearchProjectSortBy.NAME), orderDirection: z.nativeEnum(SortDirection).optional().default(SortDirection.ASC), name: z diff --git a/backend/src/server/routes/v1/secret-sync-routers/index.ts b/backend/src/server/routes/v1/secret-sync-routers/index.ts index 4675a1a40..67e1ac720 100644 --- a/backend/src/server/routes/v1/secret-sync-routers/index.ts +++ b/backend/src/server/routes/v1/secret-sync-routers/index.ts @@ -22,6 +22,7 @@ import { registerTeamCitySyncRouter } from "./teamcity-sync-router"; import { registerTerraformCloudSyncRouter } from "./terraform-cloud-sync-router"; import { registerVercelSyncRouter } from "./vercel-sync-router"; import { registerWindmillSyncRouter } from "./windmill-sync-router"; +import { registerZabbixSyncRouter } from "./zabbix-sync-router"; export * from "./secret-sync-router"; @@ -47,5 +48,6 @@ export const SECRET_SYNC_REGISTER_ROUTER_MAP: Record { diff --git a/backend/src/server/routes/v1/secret-sync-routers/zabbix-sync-router.ts b/backend/src/server/routes/v1/secret-sync-routers/zabbix-sync-router.ts new file mode 100644 index 000000000..cfd029623 --- /dev/null +++ b/backend/src/server/routes/v1/secret-sync-routers/zabbix-sync-router.ts @@ -0,0 +1,13 @@ +import { SecretSync } from "@app/services/secret-sync/secret-sync-enums"; +import { CreateZabbixSyncSchema, UpdateZabbixSyncSchema, ZabbixSyncSchema } from "@app/services/secret-sync/zabbix"; + +import { registerSyncSecretsEndpoints } from "./secret-sync-endpoints"; + +export const registerZabbixSyncRouter = async (server: FastifyZodProvider) => + registerSyncSecretsEndpoints({ + destination: SecretSync.Zabbix, + server, + responseSchema: ZabbixSyncSchema, + createSchema: CreateZabbixSyncSchema, + updateSchema: UpdateZabbixSyncSchema + }); diff --git a/backend/src/server/routes/v1/sso-router.ts b/backend/src/server/routes/v1/sso-router.ts index b6b3cb8aa..5e2518362 100644 --- a/backend/src/server/routes/v1/sso-router.ts +++ b/backend/src/server/routes/v1/sso-router.ts @@ -9,6 +9,7 @@ import { Authenticator } from "@fastify/passport"; import fastifySession from "@fastify/session"; import RedisStore from "connect-redis"; +import { CronJob } from "cron"; import { Strategy as GitLabStrategy } from "passport-gitlab2"; import { Strategy as GoogleStrategy } from "passport-google-oauth20"; import { Strategy as OAuth2Strategy } from "passport-oauth2"; @@ -25,27 +26,14 @@ import { AuthMethod } from "@app/services/auth/auth-type"; import { OrgAuthMethod } from "@app/services/org/org-types"; import { getServerCfg } from "@app/services/super-admin/super-admin-service"; -export const registerSsoRouter = async (server: FastifyZodProvider) => { +const passport = new Authenticator({ key: "sso", userProperty: "passportUser" }); + +let serverInstance: FastifyZodProvider | null = null; + +export const registerOauthMiddlewares = (server: FastifyZodProvider) => { + serverInstance = server; const appCfg = getConfig(); - const passport = new Authenticator({ key: "sso", userProperty: "passportUser" }); - const redisStore = new RedisStore({ - client: server.redis, - prefix: "oauth-session:", - ttl: 600 // 10 minutes - }); - - await server.register(fastifySession, { - secret: appCfg.COOKIE_SECRET_SIGN_KEY, - store: redisStore, - cookie: { - secure: appCfg.HTTPS_ENABLED, - sameSite: "lax" // we want cookies to be sent to Infisical in redirects originating from IDP server - } - }); - await server.register(passport.initialize()); - await server.register(passport.secureSession()); - // passport oauth strategy for Google const isGoogleOauthActive = Boolean(appCfg.CLIENT_ID_GOOGLE_LOGIN && appCfg.CLIENT_SECRET_GOOGLE_LOGIN); if (isGoogleOauthActive) { @@ -176,6 +164,49 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { ) ); } +}; + +export const refreshOauthConfig = () => { + if (!serverInstance) { + logger.warn("Cannot refresh OAuth config: server instance not available"); + return; + } + + logger.info("Refreshing OAuth configuration..."); + registerOauthMiddlewares(serverInstance); +}; + +export const initializeOauthConfigSync = async () => { + logger.info("Setting up background sync process for oauth configuration"); + + // sync every 5 minutes + const job = new CronJob("*/5 * * * *", refreshOauthConfig); + job.start(); + + return job; +}; + +export const registerSsoRouter = async (server: FastifyZodProvider) => { + const appCfg = getConfig(); + + const redisStore = new RedisStore({ + client: server.redis, + prefix: "oauth-session:", + ttl: 600 // 10 minutes + }); + + await server.register(fastifySession, { + secret: appCfg.COOKIE_SECRET_SIGN_KEY, + store: redisStore, + cookie: { + secure: appCfg.HTTPS_ENABLED, + sameSite: "lax" // we want cookies to be sent to Infisical in redirects originating from IDP server + } + }); + await server.register(passport.initialize()); + await server.register(passport.secureSession()); + + registerOauthMiddlewares(server); server.route({ url: "/redirect/google", diff --git a/backend/src/server/routes/v2/organization-router.ts b/backend/src/server/routes/v2/organization-router.ts index 504359726..fd60316db 100644 --- a/backend/src/server/routes/v2/organization-router.ts +++ b/backend/src/server/routes/v2/organization-router.ts @@ -4,7 +4,6 @@ import { OrgMembershipsSchema, ProjectMembershipsSchema, ProjectsSchema, - ProjectType, UserEncryptionKeysSchema, UsersSchema } from "@app/db/schemas"; @@ -85,9 +84,6 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { params: z.object({ organizationId: z.string().trim().describe(ORGANIZATIONS.GET_PROJECTS.organizationId) }), - querystring: z.object({ - type: z.nativeEnum(ProjectType).optional().describe(ORGANIZATIONS.GET_PROJECTS.type) - }), response: { 200: z.object({ workspaces: z @@ -114,8 +110,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { actorId: req.permission.id, actorOrgId: req.permission.orgId, actorAuthMethod: req.permission.authMethod, - orgId: req.params.organizationId, - type: req.query.type + orgId: req.params.organizationId }); return { workspaces }; diff --git a/backend/src/services/app-connection/app-connection-enums.ts b/backend/src/services/app-connection/app-connection-enums.ts index 11b84b5ad..8e71f2285 100644 --- a/backend/src/services/app-connection/app-connection-enums.ts +++ b/backend/src/services/app-connection/app-connection-enums.ts @@ -27,7 +27,8 @@ export enum AppConnection { Render = "render", Flyio = "flyio", GitLab = "gitlab", - Cloudflare = "cloudflare" + Cloudflare = "cloudflare", + Zabbix = "zabbix" } export enum AWSRegion { diff --git a/backend/src/services/app-connection/app-connection-fns.ts b/backend/src/services/app-connection/app-connection-fns.ts index 2a4d59332..55d92db35 100644 --- a/backend/src/services/app-connection/app-connection-fns.ts +++ b/backend/src/services/app-connection/app-connection-fns.ts @@ -105,6 +105,7 @@ import { validateWindmillConnectionCredentials, WindmillConnectionMethod } from "./windmill"; +import { getZabbixConnectionListItem, validateZabbixConnectionCredentials, ZabbixConnectionMethod } from "./zabbix"; export const listAppConnectionOptions = () => { return [ @@ -136,7 +137,8 @@ export const listAppConnectionOptions = () => { getRenderConnectionListItem(), getFlyioConnectionListItem(), getGitLabConnectionListItem(), - getCloudflareConnectionListItem() + getCloudflareConnectionListItem(), + getZabbixConnectionListItem() ].sort((a, b) => a.name.localeCompare(b.name)); }; @@ -216,7 +218,8 @@ export const validateAppConnectionCredentials = async ( [AppConnection.Render]: validateRenderConnectionCredentials as TAppConnectionCredentialsValidator, [AppConnection.Flyio]: validateFlyioConnectionCredentials as TAppConnectionCredentialsValidator, [AppConnection.GitLab]: validateGitLabConnectionCredentials as TAppConnectionCredentialsValidator, - [AppConnection.Cloudflare]: validateCloudflareConnectionCredentials as TAppConnectionCredentialsValidator + [AppConnection.Cloudflare]: validateCloudflareConnectionCredentials as TAppConnectionCredentialsValidator, + [AppConnection.Zabbix]: validateZabbixConnectionCredentials as TAppConnectionCredentialsValidator }; return VALIDATE_APP_CONNECTION_CREDENTIALS_MAP[appConnection.app](appConnection); @@ -253,6 +256,7 @@ export const getAppConnectionMethodName = (method: TAppConnection["method"]) => case VercelConnectionMethod.ApiToken: case OnePassConnectionMethod.ApiToken: case CloudflareConnectionMethod.APIToken: + case ZabbixConnectionMethod.ApiToken: return "API Token"; case PostgresConnectionMethod.UsernameAndPassword: case MsSqlConnectionMethod.UsernameAndPassword: @@ -332,7 +336,8 @@ export const TRANSITION_CONNECTION_CREDENTIALS_TO_PLATFORM: Record< [AppConnection.Render]: platformManagedCredentialsNotSupported, [AppConnection.Flyio]: platformManagedCredentialsNotSupported, [AppConnection.GitLab]: platformManagedCredentialsNotSupported, - [AppConnection.Cloudflare]: platformManagedCredentialsNotSupported + [AppConnection.Cloudflare]: platformManagedCredentialsNotSupported, + [AppConnection.Zabbix]: platformManagedCredentialsNotSupported }; export const enterpriseAppCheck = async ( diff --git a/backend/src/services/app-connection/app-connection-maps.ts b/backend/src/services/app-connection/app-connection-maps.ts index 9c0a3b5b8..342e39d71 100644 --- a/backend/src/services/app-connection/app-connection-maps.ts +++ b/backend/src/services/app-connection/app-connection-maps.ts @@ -29,7 +29,8 @@ export const APP_CONNECTION_NAME_MAP: Record = { [AppConnection.Render]: "Render", [AppConnection.Flyio]: "Fly.io", [AppConnection.GitLab]: "GitLab", - [AppConnection.Cloudflare]: "Cloudflare" + [AppConnection.Cloudflare]: "Cloudflare", + [AppConnection.Zabbix]: "Zabbix" }; export const APP_CONNECTION_PLAN_MAP: Record = { @@ -61,5 +62,6 @@ export const APP_CONNECTION_PLAN_MAP: Record>>; @@ -232,6 +239,7 @@ export type TAppConnectionInput = { id: string } & ( | TFlyioConnectionInput | TGitLabConnectionInput | TCloudflareConnectionInput + | TZabbixConnectionInput ); export type TSqlConnectionInput = @@ -275,7 +283,8 @@ export type TAppConnectionConfig = | TRenderConnectionConfig | TFlyioConnectionConfig | TGitLabConnectionConfig - | TCloudflareConnectionConfig; + | TCloudflareConnectionConfig + | TZabbixConnectionConfig; export type TValidateAppConnectionCredentialsSchema = | TValidateAwsConnectionCredentialsSchema @@ -306,7 +315,8 @@ export type TValidateAppConnectionCredentialsSchema = | TValidateRenderConnectionCredentialsSchema | TValidateFlyioConnectionCredentialsSchema | TValidateGitLabConnectionCredentialsSchema - | TValidateCloudflareConnectionCredentialsSchema; + | TValidateCloudflareConnectionCredentialsSchema + | TValidateZabbixConnectionCredentialsSchema; export type TListAwsConnectionKmsKeys = { connectionId: string; diff --git a/backend/src/services/app-connection/zabbix/index.ts b/backend/src/services/app-connection/zabbix/index.ts new file mode 100644 index 000000000..0de17bde7 --- /dev/null +++ b/backend/src/services/app-connection/zabbix/index.ts @@ -0,0 +1,4 @@ +export * from "./zabbix-connection-enums"; +export * from "./zabbix-connection-fns"; +export * from "./zabbix-connection-schemas"; +export * from "./zabbix-connection-types"; diff --git a/backend/src/services/app-connection/zabbix/zabbix-connection-enums.ts b/backend/src/services/app-connection/zabbix/zabbix-connection-enums.ts new file mode 100644 index 000000000..690d7c609 --- /dev/null +++ b/backend/src/services/app-connection/zabbix/zabbix-connection-enums.ts @@ -0,0 +1,3 @@ +export enum ZabbixConnectionMethod { + ApiToken = "api-token" +} diff --git a/backend/src/services/app-connection/zabbix/zabbix-connection-fns.ts b/backend/src/services/app-connection/zabbix/zabbix-connection-fns.ts new file mode 100644 index 000000000..a34a6c381 --- /dev/null +++ b/backend/src/services/app-connection/zabbix/zabbix-connection-fns.ts @@ -0,0 +1,108 @@ +import { AxiosError } from "axios"; +import RE2 from "re2"; + +import { request } from "@app/lib/config/request"; +import { BadRequestError } from "@app/lib/errors"; +import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator"; +import { AppConnection } from "@app/services/app-connection/app-connection-enums"; + +import { ZabbixConnectionMethod } from "./zabbix-connection-enums"; +import { + TZabbixConnection, + TZabbixConnectionConfig, + TZabbixHost, + TZabbixHostListResponse +} from "./zabbix-connection-types"; + +const TRAILING_SLASH_REGEX = new RE2("/+$"); + +export const getZabbixConnectionListItem = () => { + return { + name: "Zabbix" as const, + app: AppConnection.Zabbix as const, + methods: Object.values(ZabbixConnectionMethod) as [ZabbixConnectionMethod.ApiToken] + }; +}; + +export const validateZabbixConnectionCredentials = async (config: TZabbixConnectionConfig) => { + const { apiToken, instanceUrl } = config.credentials; + await blockLocalAndPrivateIpAddresses(instanceUrl); + + try { + const apiUrl = `${instanceUrl.replace(TRAILING_SLASH_REGEX, "")}/api_jsonrpc.php`; + + const payload = { + jsonrpc: "2.0", + method: "authentication.get", + params: { + output: "extend" + }, + id: 1 + }; + + const response: { data: { error?: { message: string }; result?: string } } = await request.post(apiUrl, payload, { + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${apiToken}` + } + }); + + if (response.data.error) { + throw new BadRequestError({ + message: response.data.error.message + }); + } + + return config.credentials; + } catch (error) { + if (error instanceof AxiosError) { + throw new BadRequestError({ + message: `Failed to connect to Zabbix instance: ${error.message}` + }); + } + throw error; + } +}; + +export const listZabbixHosts = async (appConnection: TZabbixConnection): Promise => { + const { apiToken, instanceUrl } = appConnection.credentials; + await blockLocalAndPrivateIpAddresses(instanceUrl); + + try { + const apiUrl = `${instanceUrl.replace(TRAILING_SLASH_REGEX, "")}/api_jsonrpc.php`; + + const payload = { + jsonrpc: "2.0", + method: "host.get", + params: { + output: ["hostid", "host"], + sortfield: "host", + sortorder: "ASC" + }, + id: 1 + }; + + const response: { data: TZabbixHostListResponse } = await request.post(apiUrl, payload, { + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${apiToken}` + } + }); + + return response.data.result + ? response.data.result.map((host) => ({ + hostId: host.hostid, + host: host.host + })) + : []; + } catch (error: unknown) { + if (error instanceof AxiosError) { + throw new BadRequestError({ + message: `Failed to validate credentials: ${error.message || "Unknown error"}` + }); + } + throw new BadRequestError({ + message: "Unable to validate connection: verify credentials" + }); + } +}; diff --git a/backend/src/services/app-connection/zabbix/zabbix-connection-schemas.ts b/backend/src/services/app-connection/zabbix/zabbix-connection-schemas.ts new file mode 100644 index 000000000..23bffd859 --- /dev/null +++ b/backend/src/services/app-connection/zabbix/zabbix-connection-schemas.ts @@ -0,0 +1,62 @@ +import z from "zod"; + +import { AppConnections } from "@app/lib/api-docs"; +import { AppConnection } from "@app/services/app-connection/app-connection-enums"; +import { + BaseAppConnectionSchema, + GenericCreateAppConnectionFieldsSchema, + GenericUpdateAppConnectionFieldsSchema +} from "@app/services/app-connection/app-connection-schemas"; + +import { ZabbixConnectionMethod } from "./zabbix-connection-enums"; + +export const ZabbixConnectionApiTokenCredentialsSchema = z.object({ + apiToken: z + .string() + .trim() + .min(1, "API Token required") + .max(1000) + .describe(AppConnections.CREDENTIALS.ZABBIX.apiToken), + instanceUrl: z.string().trim().url("Invalid Instance URL").describe(AppConnections.CREDENTIALS.ZABBIX.instanceUrl) +}); + +const BaseZabbixConnectionSchema = BaseAppConnectionSchema.extend({ app: z.literal(AppConnection.Zabbix) }); + +export const ZabbixConnectionSchema = BaseZabbixConnectionSchema.extend({ + method: z.literal(ZabbixConnectionMethod.ApiToken), + credentials: ZabbixConnectionApiTokenCredentialsSchema +}); + +export const SanitizedZabbixConnectionSchema = z.discriminatedUnion("method", [ + BaseZabbixConnectionSchema.extend({ + method: z.literal(ZabbixConnectionMethod.ApiToken), + credentials: ZabbixConnectionApiTokenCredentialsSchema.pick({ instanceUrl: true }) + }) +]); + +export const ValidateZabbixConnectionCredentialsSchema = z.discriminatedUnion("method", [ + z.object({ + method: z.literal(ZabbixConnectionMethod.ApiToken).describe(AppConnections.CREATE(AppConnection.Zabbix).method), + credentials: ZabbixConnectionApiTokenCredentialsSchema.describe( + AppConnections.CREATE(AppConnection.Zabbix).credentials + ) + }) +]); + +export const CreateZabbixConnectionSchema = ValidateZabbixConnectionCredentialsSchema.and( + GenericCreateAppConnectionFieldsSchema(AppConnection.Zabbix) +); + +export const UpdateZabbixConnectionSchema = z + .object({ + credentials: ZabbixConnectionApiTokenCredentialsSchema.optional().describe( + AppConnections.UPDATE(AppConnection.Zabbix).credentials + ) + }) + .and(GenericUpdateAppConnectionFieldsSchema(AppConnection.Zabbix)); + +export const ZabbixConnectionListItemSchema = z.object({ + name: z.literal("Zabbix"), + app: z.literal(AppConnection.Zabbix), + methods: z.nativeEnum(ZabbixConnectionMethod).array() +}); diff --git a/backend/src/services/app-connection/zabbix/zabbix-connection-service.ts b/backend/src/services/app-connection/zabbix/zabbix-connection-service.ts new file mode 100644 index 000000000..e8c8f8018 --- /dev/null +++ b/backend/src/services/app-connection/zabbix/zabbix-connection-service.ts @@ -0,0 +1,30 @@ +import { logger } from "@app/lib/logger"; +import { OrgServiceActor } from "@app/lib/types"; + +import { AppConnection } from "../app-connection-enums"; +import { listZabbixHosts } from "./zabbix-connection-fns"; +import { TZabbixConnection } from "./zabbix-connection-types"; + +type TGetAppConnectionFunc = ( + app: AppConnection, + connectionId: string, + actor: OrgServiceActor +) => Promise; + +export const zabbixConnectionService = (getAppConnection: TGetAppConnectionFunc) => { + const listHosts = async (connectionId: string, actor: OrgServiceActor) => { + const appConnection = await getAppConnection(AppConnection.Zabbix, connectionId, actor); + + try { + const hosts = await listZabbixHosts(appConnection); + return hosts; + } catch (error) { + logger.error(error, "Failed to establish connection with zabbix"); + return []; + } + }; + + return { + listHosts + }; +}; diff --git a/backend/src/services/app-connection/zabbix/zabbix-connection-types.ts b/backend/src/services/app-connection/zabbix/zabbix-connection-types.ts new file mode 100644 index 000000000..08b4c685f --- /dev/null +++ b/backend/src/services/app-connection/zabbix/zabbix-connection-types.ts @@ -0,0 +1,33 @@ +import z from "zod"; + +import { DiscriminativePick } from "@app/lib/types"; + +import { AppConnection } from "../app-connection-enums"; +import { + CreateZabbixConnectionSchema, + ValidateZabbixConnectionCredentialsSchema, + ZabbixConnectionSchema +} from "./zabbix-connection-schemas"; + +export type TZabbixConnection = z.infer; + +export type TZabbixConnectionInput = z.infer & { + app: AppConnection.Zabbix; +}; + +export type TValidateZabbixConnectionCredentialsSchema = typeof ValidateZabbixConnectionCredentialsSchema; + +export type TZabbixConnectionConfig = DiscriminativePick & { + orgId: string; +}; + +export type TZabbixHost = { + hostId: string; + host: string; +}; + +export type TZabbixHostListResponse = { + jsonrpc: string; + result: { hostid: string; host: string }[]; + error?: { message: string }; +}; diff --git a/backend/src/services/certificate-authority/certificate-authority-service.ts b/backend/src/services/certificate-authority/certificate-authority-service.ts index 8a2aa0a5d..0f30e91c3 100644 --- a/backend/src/services/certificate-authority/certificate-authority-service.ts +++ b/backend/src/services/certificate-authority/certificate-authority-service.ts @@ -1,6 +1,6 @@ import { ForbiddenError } from "@casl/ability"; -import { ActionProjectType, ProjectType, TableName } from "@app/db/schemas"; +import { TableName } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { BadRequestError, NotFoundError } from "@app/lib/errors"; @@ -50,10 +50,7 @@ type TCertificateAuthorityServiceFactoryDep = { >; externalCertificateAuthorityDAL: Pick; internalCertificateAuthorityService: TInternalCertificateAuthorityServiceFactory; - projectDAL: Pick< - TProjectDALFactory, - "findProjectBySlug" | "findOne" | "updateById" | "findById" | "transaction" | "getProjectFromSplitId" - >; + projectDAL: Pick; permissionService: Pick; certificateDAL: Pick; certificateBodyDAL: Pick; @@ -98,23 +95,12 @@ export const certificateAuthorityServiceFactory = ({ { type, projectId, name, enableDirectIssuance, configuration, status }: TCreateCertificateAuthorityDTO, actor: OrgServiceActor ) => { - let finalProjectId: string = projectId; - const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId( - projectId, - ProjectType.CertificateManager - ); - - if (certManagerProjectFromSplit) { - finalProjectId = certManagerProjectFromSplit.id; - } - const { permission } = await permissionService.getProjectPermission({ actor: actor.type, actorId: actor.id, - projectId: finalProjectId, + projectId, actorAuthMethod: actor.authMethod, - actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId: actor.orgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -126,7 +112,7 @@ export const certificateAuthorityServiceFactory = ({ const ca = await internalCertificateAuthorityService.createCa({ ...(configuration as TCreateInternalCertificateAuthorityDTO["configuration"]), isInternal: true, - projectId: finalProjectId, + projectId, enableDirectIssuance, name }); @@ -142,7 +128,7 @@ export const certificateAuthorityServiceFactory = ({ type, enableDirectIssuance: ca.enableDirectIssuance, name: ca.name, - projectId: finalProjectId, + projectId, status, configuration: ca.internalCa } as TCertificateAuthority; @@ -151,7 +137,7 @@ export const certificateAuthorityServiceFactory = ({ if (type === CaType.ACME) { return acmeFns.createCertificateAuthority({ name, - projectId: finalProjectId, + projectId, configuration: configuration as TCreateAcmeCertificateAuthorityDTO["configuration"], enableDirectIssuance, status, @@ -181,8 +167,7 @@ export const certificateAuthorityServiceFactory = ({ actorId: actor.id, projectId: certificateAuthority.projectId, actorAuthMethod: actor.authMethod, - actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId: actor.orgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -225,23 +210,12 @@ export const certificateAuthorityServiceFactory = ({ { projectId, type }: { projectId: string; type: CaType }, actor: OrgServiceActor ) => { - let finalProjectId: string = projectId; - const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId( - projectId, - ProjectType.CertificateManager - ); - - if (certManagerProjectFromSplit) { - finalProjectId = certManagerProjectFromSplit.id; - } - const { permission } = await permissionService.getProjectPermission({ actor: actor.type, actorId: actor.id, - projectId: finalProjectId, + projectId, actorAuthMethod: actor.authMethod, - actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId: actor.orgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -251,7 +225,7 @@ export const certificateAuthorityServiceFactory = ({ if (type === CaType.INTERNAL) { const cas = await certificateAuthorityDAL.findWithAssociatedCa({ - [`${TableName.CertificateAuthority}.projectId` as "projectId"]: finalProjectId, + [`${TableName.CertificateAuthority}.projectId` as "projectId"]: projectId, $notNull: [`${TableName.InternalCertificateAuthority}.id` as "id"] }); @@ -269,7 +243,7 @@ export const certificateAuthorityServiceFactory = ({ } if (type === CaType.ACME) { - return acmeFns.listCertificateAuthorities({ projectId: finalProjectId }); + return acmeFns.listCertificateAuthorities({ projectId }); } throw new BadRequestError({ message: "Invalid certificate authority type" }); @@ -294,8 +268,7 @@ export const certificateAuthorityServiceFactory = ({ actorId: actor.id, projectId: certificateAuthority.projectId, actorAuthMethod: actor.authMethod, - actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId: actor.orgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -368,8 +341,7 @@ export const certificateAuthorityServiceFactory = ({ actorId: actor.id, projectId: certificateAuthority.projectId, actorAuthMethod: actor.authMethod, - actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId: actor.orgId }); ForbiddenError.from(permission).throwUnlessCan( diff --git a/backend/src/services/certificate-authority/internal/internal-certificate-authority-service.ts b/backend/src/services/certificate-authority/internal/internal-certificate-authority-service.ts index cbc32c55a..f6370db61 100644 --- a/backend/src/services/certificate-authority/internal/internal-certificate-authority-service.ts +++ b/backend/src/services/certificate-authority/internal/internal-certificate-authority-service.ts @@ -4,13 +4,7 @@ import * as x509 from "@peculiar/x509"; import slugify from "@sindresorhus/slugify"; import { z } from "zod"; -import { - ActionProjectType, - ProjectType, - TableName, - TCertificateAuthorities, - TCertificateTemplates -} from "@app/db/schemas"; +import { TableName, TCertificateAuthorities, TCertificateTemplates } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionActions, @@ -105,10 +99,7 @@ type TInternalCertificateAuthorityServiceFactoryDep = { certificateBodyDAL: Pick; pkiCollectionDAL: Pick; pkiCollectionItemDAL: Pick; - projectDAL: Pick< - TProjectDALFactory, - "findProjectBySlug" | "findOne" | "updateById" | "findById" | "transaction" | "getProjectFromSplitId" - >; + projectDAL: Pick; kmsService: Pick; permissionService: Pick; }; @@ -154,21 +145,12 @@ export const internalCertificateAuthorityServiceFactory = ({ if (!project) throw new NotFoundError({ message: `Project with slug '${dto.projectSlug}' not found` }); projectId = project.id; - const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId( - projectId, - ProjectType.CertificateManager - ); - if (certManagerProjectFromSplit) { - projectId = certManagerProjectFromSplit.id; - } - const { permission } = await permissionService.getProjectPermission({ actor: dto.actor, actorId: dto.actorId, projectId, actorAuthMethod: dto.actorAuthMethod, - actorOrgId: dto.actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId: dto.actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -351,8 +333,7 @@ export const internalCertificateAuthorityServiceFactory = ({ actorId, projectId: ca.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, @@ -376,8 +357,7 @@ export const internalCertificateAuthorityServiceFactory = ({ actorId: dto.actorId, projectId: ca.projectId, actorAuthMethod: dto.actorAuthMethod, - actorOrgId: dto.actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId: dto.actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -409,8 +389,7 @@ export const internalCertificateAuthorityServiceFactory = ({ actorId, projectId: ca.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -435,8 +414,7 @@ export const internalCertificateAuthorityServiceFactory = ({ actorId, projectId: ca.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -499,8 +477,7 @@ export const internalCertificateAuthorityServiceFactory = ({ actorId, projectId: ca.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -786,8 +763,7 @@ export const internalCertificateAuthorityServiceFactory = ({ actorId, projectId: ca.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -823,8 +799,7 @@ export const internalCertificateAuthorityServiceFactory = ({ actorId, projectId: ca.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -904,8 +879,7 @@ export const internalCertificateAuthorityServiceFactory = ({ actorId, projectId: ca.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -1052,8 +1026,7 @@ export const internalCertificateAuthorityServiceFactory = ({ actorId, projectId: ca.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -1224,8 +1197,7 @@ export const internalCertificateAuthorityServiceFactory = ({ actorId, projectId: ca.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -1581,8 +1553,7 @@ export const internalCertificateAuthorityServiceFactory = ({ actorId: dto.actorId, projectId: ca.projectId, actorAuthMethod: dto.actorAuthMethod, - actorOrgId: dto.actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId: dto.actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -1949,8 +1920,7 @@ export const internalCertificateAuthorityServiceFactory = ({ actorId, projectId: ca.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); const certificateTemplates = await certificateTemplateDAL.find({ caId }); diff --git a/backend/src/services/certificate-template/certificate-template-service.ts b/backend/src/services/certificate-template/certificate-template-service.ts index 20c061bf7..f8e1cf788 100644 --- a/backend/src/services/certificate-template/certificate-template-service.ts +++ b/backend/src/services/certificate-template/certificate-template-service.ts @@ -1,7 +1,7 @@ import { ForbiddenError, subject } from "@casl/ability"; import * as x509 from "@peculiar/x509"; -import { ActionProjectType, TCertificateTemplateEstConfigsUpdate } from "@app/db/schemas"; +import { TCertificateTemplateEstConfigsUpdate } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { @@ -76,8 +76,7 @@ export const certificateTemplateServiceFactory = ({ actorId, projectId: ca.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -138,8 +137,7 @@ export const certificateTemplateServiceFactory = ({ actorId, projectId: certTemplate.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -203,8 +201,7 @@ export const certificateTemplateServiceFactory = ({ actorId, projectId: certTemplate.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -230,8 +227,7 @@ export const certificateTemplateServiceFactory = ({ actorId, projectId: certTemplate.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -272,8 +268,7 @@ export const certificateTemplateServiceFactory = ({ actorId, projectId: certTemplate.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -355,8 +350,7 @@ export const certificateTemplateServiceFactory = ({ actorId, projectId: certTemplate.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -435,8 +429,7 @@ export const certificateTemplateServiceFactory = ({ actorId: dto.actorId, projectId: certTemplate.projectId, actorAuthMethod: dto.actorAuthMethod, - actorOrgId: dto.actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId: dto.actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( diff --git a/backend/src/services/certificate/certificate-service.ts b/backend/src/services/certificate/certificate-service.ts index 36842f324..753ee0cda 100644 --- a/backend/src/services/certificate/certificate-service.ts +++ b/backend/src/services/certificate/certificate-service.ts @@ -1,7 +1,6 @@ import { ForbiddenError } from "@casl/ability"; import * as x509 from "@peculiar/x509"; -import { ActionProjectType, ProjectType } from "@app/db/schemas"; import { TCertificateAuthorityCrlDALFactory } from "@app/ee/services/certificate-authority-crl/certificate-authority-crl-dal"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { @@ -48,10 +47,7 @@ type TCertificateServiceFactoryDep = { certificateAuthoritySecretDAL: Pick; pkiCollectionDAL: Pick; pkiCollectionItemDAL: Pick; - projectDAL: Pick< - TProjectDALFactory, - "findProjectBySlug" | "findOne" | "updateById" | "findById" | "transaction" | "getProjectFromSplitId" - >; + projectDAL: Pick; kmsService: Pick; permissionService: Pick; }; @@ -83,8 +79,7 @@ export const certificateServiceFactory = ({ actorId, projectId: cert.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -114,8 +109,7 @@ export const certificateServiceFactory = ({ actorId, projectId: cert.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -148,8 +142,7 @@ export const certificateServiceFactory = ({ actorId, projectId: cert.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -198,8 +191,7 @@ export const certificateServiceFactory = ({ actorId, projectId: ca.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -247,8 +239,7 @@ export const certificateServiceFactory = ({ actorId, projectId: cert.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -321,23 +312,14 @@ export const certificateServiceFactory = ({ const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId); if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` }); - let projectId = project.id; - - const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId( - projectId, - ProjectType.CertificateManager - ); - if (certManagerProjectFromSplit) { - projectId = certManagerProjectFromSplit.id; - } + const projectId = project.id; const { permission } = await permissionService.getProjectPermission({ actor, actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -541,8 +523,7 @@ export const certificateServiceFactory = ({ actorId, projectId: cert.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( diff --git a/backend/src/services/cmek/cmek-service.ts b/backend/src/services/cmek/cmek-service.ts index fd6cbf39d..7817266b3 100644 --- a/backend/src/services/cmek/cmek-service.ts +++ b/backend/src/services/cmek/cmek-service.ts @@ -1,6 +1,5 @@ import { ForbiddenError } from "@casl/ability"; -import { ActionProjectType, ProjectType } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionCmekActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { SigningAlgorithm } from "@app/lib/crypto/sign"; @@ -23,32 +22,23 @@ import { TKmsKeyDALFactory } from "@app/services/kms/kms-key-dal"; import { TKmsServiceFactory } from "@app/services/kms/kms-service"; import { KmsKeyUsage } from "../kms/kms-types"; -import { TProjectDALFactory } from "../project/project-dal"; type TCmekServiceFactoryDep = { kmsService: TKmsServiceFactory; kmsDAL: TKmsKeyDALFactory; permissionService: TPermissionServiceFactory; - projectDAL: Pick; }; export type TCmekServiceFactory = ReturnType; -export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, projectDAL }: TCmekServiceFactoryDep) => { - const createCmek = async ({ projectId: preSplitProjectId, ...dto }: TCreateCmekDTO, actor: OrgServiceActor) => { - let projectId = preSplitProjectId; - const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(projectId, ProjectType.KMS); - if (cmekProjectFromSplit) { - projectId = cmekProjectFromSplit.id; - } - +export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService }: TCmekServiceFactoryDep) => { + const createCmek = async ({ projectId, ...dto }: TCreateCmekDTO, actor: OrgServiceActor) => { const { permission } = await permissionService.getProjectPermission({ actor: actor.type, actorId: actor.id, projectId, actorAuthMethod: actor.authMethod, - actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.KMS + actorOrgId: actor.orgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Create, ProjectPermissionSub.Cmek); @@ -87,8 +77,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj actorId: actor.id, projectId: key.projectId, actorAuthMethod: actor.authMethod, - actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.KMS + actorOrgId: actor.orgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Edit, ProjectPermissionSub.Cmek); @@ -124,8 +113,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj actorId: actor.id, projectId: key.projectId, actorAuthMethod: actor.authMethod, - actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.KMS + actorOrgId: actor.orgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Delete, ProjectPermissionSub.Cmek); @@ -135,23 +123,13 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj return key; }; - const listCmeksByProjectId = async ( - { projectId: preSplitProjectId, ...filters }: TListCmeksByProjectIdDTO, - actor: OrgServiceActor - ) => { - let projectId = preSplitProjectId; - const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(preSplitProjectId, ProjectType.KMS); - if (cmekProjectFromSplit) { - projectId = cmekProjectFromSplit.id; - } - + const listCmeksByProjectId = async ({ projectId, ...filters }: TListCmeksByProjectIdDTO, actor: OrgServiceActor) => { const { permission } = await permissionService.getProjectPermission({ actor: actor.type, actorId: actor.id, projectId, actorAuthMethod: actor.authMethod, - actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.KMS + actorOrgId: actor.orgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek); @@ -173,8 +151,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj actorId: actor.id, projectId: key.projectId, actorAuthMethod: actor.authMethod, - actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.KMS + actorOrgId: actor.orgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek); @@ -195,8 +172,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj actorId: actor.id, projectId: key.projectId, actorAuthMethod: actor.authMethod, - actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.KMS + actorOrgId: actor.orgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek); @@ -218,8 +194,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj actorId: actor.id, projectId: key.projectId, actorAuthMethod: actor.authMethod, - actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.KMS + actorOrgId: actor.orgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Encrypt, ProjectPermissionSub.Cmek); @@ -246,8 +221,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj actorId: actor.id, projectId: key.projectId, actorAuthMethod: actor.authMethod, - actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.KMS + actorOrgId: actor.orgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek); @@ -294,8 +268,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj actorId: actor.id, projectId: key.projectId, actorAuthMethod: actor.authMethod, - actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.KMS + actorOrgId: actor.orgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek); @@ -318,8 +291,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj actorId: actor.id, projectId: key.projectId, actorAuthMethod: actor.authMethod, - actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.KMS + actorOrgId: actor.orgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Sign, ProjectPermissionSub.Cmek); @@ -353,8 +325,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj actorId: actor.id, projectId: key.projectId, actorAuthMethod: actor.authMethod, - actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.KMS + actorOrgId: actor.orgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Verify, ProjectPermissionSub.Cmek); @@ -389,8 +360,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj actorId: actor.id, projectId: key.projectId, actorAuthMethod: actor.authMethod, - actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.KMS + actorOrgId: actor.orgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Decrypt, ProjectPermissionSub.Cmek); diff --git a/backend/src/services/folder-commit/folder-commit-service.test.ts b/backend/src/services/folder-commit/folder-commit-service.test.ts index 1879cf493..28d603829 100644 --- a/backend/src/services/folder-commit/folder-commit-service.test.ts +++ b/backend/src/services/folder-commit/folder-commit-service.test.ts @@ -4,7 +4,7 @@ import { Knex } from "knex"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { ProjectType, TSecretFolderVersions, TSecretVersionsV2 } from "@app/db/schemas"; +import { TSecretFolderVersions, TSecretVersionsV2 } from "@app/db/schemas"; import { BadRequestError, NotFoundError } from "@app/lib/errors"; import { ActorType } from "../auth/auth-type"; @@ -433,8 +433,7 @@ describe("folderCommitServiceFactory", () => { mockFolderCommitDAL.findCommitsToRecreate.mockResolvedValue([]); mockProjectDAL.findProjectByEnvId.mockResolvedValue({ id: "project-id", - name: "test-project", - type: ProjectType.SecretManager + name: "test-project" }); // Act diff --git a/backend/src/services/folder-commit/folder-commit-service.ts b/backend/src/services/folder-commit/folder-commit-service.ts index 35f032312..3576f444b 100644 --- a/backend/src/services/folder-commit/folder-commit-service.ts +++ b/backend/src/services/folder-commit/folder-commit-service.ts @@ -2,13 +2,7 @@ import { ForbiddenError } from "@casl/ability"; import { Knex } from "knex"; -import { - ActionProjectType, - TSecretFolders, - TSecretFolderVersions, - TSecretV2TagJunctionInsert, - TSecretVersionsV2 -} from "@app/db/schemas"; +import { TSecretFolders, TSecretFolderVersions, TSecretV2TagJunctionInsert, TSecretVersionsV2 } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionCommitsActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { getConfig } from "@app/lib/config/env"; @@ -223,8 +217,7 @@ export const folderCommitServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCommitsActions.Read, ProjectPermissionSub.Commits); @@ -2067,8 +2060,7 @@ export const folderCommitServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( diff --git a/backend/src/services/group-project/group-project-service.ts b/backend/src/services/group-project/group-project-service.ts index 29a7787be..21b8efbb8 100644 --- a/backend/src/services/group-project/group-project-service.ts +++ b/backend/src/services/group-project/group-project-service.ts @@ -1,6 +1,6 @@ import { ForbiddenError } from "@casl/ability"; -import { ActionProjectType, ProjectMembershipRole, SecretKeyEncoding, TGroups } from "@app/db/schemas"; +import { ProjectMembershipRole, SecretKeyEncoding, TGroups } from "@app/db/schemas"; import { TListProjectGroupUsersDTO } from "@app/ee/services/group/group-types"; import { constructPermissionErrorMessage, @@ -78,8 +78,7 @@ export const groupProjectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionGroupActions.Create, ProjectPermissionSub.Groups); @@ -269,8 +268,7 @@ export const groupProjectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionGroupActions.Edit, ProjectPermissionSub.Groups); @@ -383,8 +381,7 @@ export const groupProjectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionGroupActions.Delete, ProjectPermissionSub.Groups); @@ -428,8 +425,7 @@ export const groupProjectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionGroupActions.Read, ProjectPermissionSub.Groups); @@ -456,8 +452,7 @@ export const groupProjectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionGroupActions.Read, ProjectPermissionSub.Groups); @@ -498,8 +493,7 @@ export const groupProjectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionGroupActions.Read, ProjectPermissionSub.Groups); diff --git a/backend/src/services/identity-project/identity-project-service.ts b/backend/src/services/identity-project/identity-project-service.ts index 81387d141..7ee051d88 100644 --- a/backend/src/services/identity-project/identity-project-service.ts +++ b/backend/src/services/identity-project/identity-project-service.ts @@ -1,6 +1,6 @@ import { ForbiddenError, subject } from "@casl/ability"; -import { ActionProjectType, ProjectMembershipRole } from "@app/db/schemas"; +import { ProjectMembershipRole } from "@app/db/schemas"; import { constructPermissionErrorMessage, validatePrivilegeChangeOperation @@ -62,8 +62,7 @@ export const identityProjectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionIdentityActions.Create, @@ -94,23 +93,25 @@ export const identityProjectServiceFactory = ({ projectId ); - const permissionBoundary = validatePrivilegeChangeOperation( - membership.shouldUseNewPrivilegeSystem, - ProjectPermissionIdentityActions.GrantPrivileges, - ProjectPermissionSub.Identity, - permission, - rolePermission - ); - if (!permissionBoundary.isValid) - throw new PermissionBoundaryError({ - message: constructPermissionErrorMessage( - "Failed to assign to role", - membership.shouldUseNewPrivilegeSystem, - ProjectPermissionIdentityActions.GrantPrivileges, - ProjectPermissionSub.Identity - ), - details: { missingPermissions: permissionBoundary.missingPermissions } - }); + if (requestedRoleChange !== ProjectMembershipRole.NoAccess) { + const permissionBoundary = validatePrivilegeChangeOperation( + membership.shouldUseNewPrivilegeSystem, + ProjectPermissionIdentityActions.GrantPrivileges, + ProjectPermissionSub.Identity, + permission, + rolePermission + ); + if (!permissionBoundary.isValid) + throw new PermissionBoundaryError({ + message: constructPermissionErrorMessage( + "Failed to assign to role", + membership.shouldUseNewPrivilegeSystem, + ProjectPermissionIdentityActions.GrantPrivileges, + ProjectPermissionSub.Identity + ), + details: { missingPermissions: permissionBoundary.missingPermissions } + }); + } } // validate custom roles input @@ -180,8 +181,7 @@ export const identityProjectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionIdentityActions.Edit, @@ -291,8 +291,7 @@ export const identityProjectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionIdentityActions.Delete, @@ -320,8 +319,7 @@ export const identityProjectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionIdentityActions.Read, @@ -354,8 +352,7 @@ export const identityProjectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -391,8 +388,7 @@ export const identityProjectServiceFactory = ({ actorId, projectId: membership.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( diff --git a/backend/src/services/identity/identity-service.ts b/backend/src/services/identity/identity-service.ts index 4ea382f9e..7c76520b3 100644 --- a/backend/src/services/identity/identity-service.ts +++ b/backend/src/services/identity/identity-service.ts @@ -69,23 +69,25 @@ export const identityServiceFactory = ({ orgId ); const isCustomRole = Boolean(customRole); - const permissionBoundary = validatePrivilegeChangeOperation( - membership.shouldUseNewPrivilegeSystem, - OrgPermissionIdentityActions.GrantPrivileges, - OrgPermissionSubjects.Identity, - permission, - rolePermission - ); - if (!permissionBoundary.isValid) - throw new PermissionBoundaryError({ - message: constructPermissionErrorMessage( - "Failed to create identity", - membership.shouldUseNewPrivilegeSystem, - OrgPermissionIdentityActions.GrantPrivileges, - OrgPermissionSubjects.Identity - ), - details: { missingPermissions: permissionBoundary.missingPermissions } - }); + if (role !== OrgMembershipRole.NoAccess) { + const permissionBoundary = validatePrivilegeChangeOperation( + membership.shouldUseNewPrivilegeSystem, + OrgPermissionIdentityActions.GrantPrivileges, + OrgPermissionSubjects.Identity, + permission, + rolePermission + ); + if (!permissionBoundary.isValid) + throw new PermissionBoundaryError({ + message: constructPermissionErrorMessage( + "Failed to create identity", + membership.shouldUseNewPrivilegeSystem, + OrgPermissionIdentityActions.GrantPrivileges, + OrgPermissionSubjects.Identity + ), + details: { missingPermissions: permissionBoundary.missingPermissions } + }); + } const plan = await licenseService.getPlan(orgId); @@ -187,6 +189,7 @@ export const identityServiceFactory = ({ ), details: { missingPermissions: appliedRolePermissionBoundary.missingPermissions } }); + if (isCustomRole) customRole = customOrgRole; } diff --git a/backend/src/services/integration-auth/integration-auth-service.ts b/backend/src/services/integration-auth/integration-auth-service.ts index ad8d43526..f49369bad 100644 --- a/backend/src/services/integration-auth/integration-auth-service.ts +++ b/backend/src/services/integration-auth/integration-auth-service.ts @@ -4,13 +4,7 @@ import { Octokit } from "@octokit/rest"; import { Client as OctopusClient, SpaceRepository as OctopusSpaceRepository } from "@octopusdeploy/api-client"; import AWS from "aws-sdk"; -import { - ActionProjectType, - SecretEncryptionAlgo, - SecretKeyEncoding, - TIntegrationAuths, - TIntegrationAuthsInsert -} from "@app/db/schemas"; +import { SecretEncryptionAlgo, SecretKeyEncoding, TIntegrationAuths, TIntegrationAuthsInsert } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { getConfig } from "@app/lib/config/env"; @@ -103,8 +97,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const authorizations = await integrationAuthDAL.find({ projectId }); @@ -122,8 +115,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: auth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); return permission.can(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations) ? auth : null; @@ -146,8 +138,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); return integrationAuth; @@ -172,8 +163,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations); @@ -291,8 +281,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations); @@ -446,8 +435,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations); @@ -744,8 +732,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); @@ -779,8 +766,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); @@ -810,8 +796,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId); @@ -852,8 +837,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId); @@ -881,8 +865,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId); @@ -956,8 +939,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId); @@ -1004,8 +986,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId); @@ -1039,8 +1020,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId); @@ -1098,8 +1078,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId); @@ -1135,8 +1114,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId); @@ -1177,8 +1155,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId); @@ -1218,8 +1195,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId); @@ -1259,8 +1235,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId); @@ -1299,8 +1274,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId); @@ -1340,8 +1314,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId); @@ -1409,8 +1382,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId); @@ -1484,8 +1456,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId); @@ -1535,8 +1506,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId); @@ -1584,8 +1554,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId); @@ -1653,8 +1622,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId); @@ -1695,8 +1663,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId); @@ -1808,8 +1775,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations); @@ -1832,8 +1798,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations); @@ -1866,8 +1831,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(sourcePermission).throwUnlessCan( @@ -1880,8 +1844,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(targetPermission).throwUnlessCan( @@ -1914,8 +1877,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); @@ -1949,8 +1911,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId); @@ -1990,8 +1951,7 @@ export const integrationAuthServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId); diff --git a/backend/src/services/integration/integration-service.ts b/backend/src/services/integration/integration-service.ts index 2ef8615eb..e03ca1e8f 100644 --- a/backend/src/services/integration/integration-service.ts +++ b/backend/src/services/integration/integration-service.ts @@ -1,6 +1,5 @@ import { ForbiddenError } from "@casl/ability"; -import { ActionProjectType } from "@app/db/schemas"; import { throwIfMissingSecretReadValueOrDescribePermission } from "@app/ee/services/permission/permission-fns"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { @@ -91,8 +90,7 @@ export const integrationServiceFactory = ({ actorId, projectId: integrationAuth.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations); @@ -167,8 +165,7 @@ export const integrationServiceFactory = ({ actorId, projectId: integration.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations); @@ -231,8 +228,7 @@ export const integrationServiceFactory = ({ actorId, projectId: integration.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); @@ -259,8 +255,7 @@ export const integrationServiceFactory = ({ actorId, projectId: integration.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); @@ -302,8 +297,7 @@ export const integrationServiceFactory = ({ actorId, projectId: integration.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations); @@ -339,8 +333,7 @@ export const integrationServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); @@ -359,8 +352,7 @@ export const integrationServiceFactory = ({ actorId, projectId: integration.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); diff --git a/backend/src/services/microsoft-teams/microsoft-teams-fns.ts b/backend/src/services/microsoft-teams/microsoft-teams-fns.ts index 4111115bf..d477143a9 100644 --- a/backend/src/services/microsoft-teams/microsoft-teams-fns.ts +++ b/backend/src/services/microsoft-teams/microsoft-teams-fns.ts @@ -400,7 +400,7 @@ export const buildTeamsPayload = (notification: TNotification) => { { type: "Action.OpenUrl", title: "View request in Infisical", - url: `${appCfg.SITE_URL}/secret-manager/${payload.projectId}/approval?requestId=${payload.requestId}` + url: `${appCfg.SITE_URL}/projects/${payload.projectId}/secret-manager/approval?requestId=${payload.requestId}` } ] }; diff --git a/backend/src/services/org-membership/org-membership-dal.ts b/backend/src/services/org-membership/org-membership-dal.ts index 68b117202..1fbe03f4b 100644 --- a/backend/src/services/org-membership/org-membership-dal.ts +++ b/backend/src/services/org-membership/org-membership-dal.ts @@ -103,8 +103,41 @@ export const orgMembershipDALFactory = (db: TDbClient) => { } }; + const findRecentInvitedMemberships = async () => { + try { + const now = new Date(); + const oneWeekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000); + const oneMonthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000); + const threeMonthsAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000); + + const memberships = await db + .replicaNode()(TableName.OrgMembership) + .where("status", "invited") + .where((qb) => { + // lastInvitedAt is null AND createdAt is between 1 week and 3 months ago + void qb + .whereNull(`${TableName.OrgMembership}.lastInvitedAt`) + .whereBetween(`${TableName.OrgMembership}.createdAt`, [threeMonthsAgo, oneWeekAgo]); + }) + .orWhere((qb) => { + // lastInvitedAt is older than 1 week ago AND createdAt is younger than 1 month ago + void qb + .where(`${TableName.OrgMembership}.lastInvitedAt`, "<", oneMonthAgo) + .where(`${TableName.OrgMembership}.createdAt`, ">", oneWeekAgo); + }); + + return memberships; + } catch (error) { + throw new DatabaseError({ + error, + name: "Find recent invited memberships" + }); + } + }; + return { ...orgMembershipOrm, - findOrgMembershipById + findOrgMembershipById, + findRecentInvitedMemberships }; }; diff --git a/backend/src/services/org/org-service.ts b/backend/src/services/org/org-service.ts index a74612803..515bf1a9b 100644 --- a/backend/src/services/org/org-service.ts +++ b/backend/src/services/org/org-service.ts @@ -4,7 +4,6 @@ import jwt from "jsonwebtoken"; import { Knex } from "knex"; import { - ActionProjectType, OrgMembershipRole, OrgMembershipStatus, ProjectMembershipRole, @@ -106,7 +105,10 @@ type TOrgServiceFactoryDep = { "findProjectMembershipsByUserId" | "delete" | "create" | "find" | "insertMany" | "transaction" >; projectKeyDAL: Pick; - orgMembershipDAL: Pick; + orgMembershipDAL: Pick< + TOrgMembershipDALFactory, + "findOrgMembershipById" | "findOne" | "findById" | "findRecentInvitedMemberships" | "updateById" + >; incidentContactDAL: TIncidentContactsDALFactory; samlConfigDAL: Pick; oidcConfigDAL: Pick; @@ -233,14 +235,14 @@ export const orgServiceFactory = ({ return org; }; - const findAllWorkspaces = async ({ actor, actorId, orgId, type }: TFindAllWorkspacesDTO) => { + const findAllWorkspaces = async ({ actor, actorId, orgId }: TFindAllWorkspacesDTO) => { if (actor === ActorType.USER) { - const workspaces = await projectDAL.findUserProjects(actorId, orgId, type || "all"); + const workspaces = await projectDAL.findUserProjects(actorId, orgId); return workspaces; } if (actor === ActorType.IDENTITY) { - const workspaces = await projectDAL.findAllProjectsByIdentity(actorId, type); + const workspaces = await projectDAL.findAllProjectsByIdentity(actorId); return workspaces; } @@ -974,8 +976,7 @@ export const orgServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(projectPermission).throwUnlessCan( ProjectPermissionMemberActions.Create, @@ -1426,6 +1427,53 @@ export const orgServiceFactory = ({ return incidentContact; }; + /** + * Re-send emails to users who haven't accepted an invite yet + */ + const notifyInvitedUsers = async () => { + const invitedUsers = await orgMembershipDAL.findRecentInvitedMemberships(); + const appCfg = getConfig(); + + const orgCache: Record = {}; + + await Promise.all( + invitedUsers.map(async (invitedUser) => { + let org = orgCache[invitedUser.orgId]; + if (!org) { + org = await orgDAL.findById(invitedUser.orgId); + orgCache[invitedUser.orgId] = org; + } + + if (!org || !invitedUser.userId) return; + + const token = await tokenService.createTokenForUser({ + type: TokenType.TOKEN_EMAIL_ORG_INVITATION, + userId: invitedUser.userId, + orgId: org.id + }); + + if (invitedUser.inviteEmail) { + await smtpService.sendMail({ + template: SmtpTemplates.OrgInvite, + subjectLine: `Reminder: You have been invited to ${org.name} on Infisical`, + recipients: [invitedUser.inviteEmail], + substitutions: { + organizationName: org.name, + email: invitedUser.inviteEmail, + organizationId: org.id.toString(), + token, + callback_url: `${appCfg.SITE_URL}/signupinvite` + } + }); + } + + await orgMembershipDAL.updateById(invitedUser.id, { + lastInvitedAt: new Date() + }); + }) + ); + }; + return { findOrganizationById, findAllOrgMembers, @@ -1449,6 +1497,7 @@ export const orgServiceFactory = ({ listProjectMembershipsByOrgMembershipId, findOrgBySlug, resendOrgMemberInvitation, - upgradePrivilegeSystem + upgradePrivilegeSystem, + notifyInvitedUsers }; }; diff --git a/backend/src/services/org/org-types.ts b/backend/src/services/org/org-types.ts index 8b2485ac4..0736f174d 100644 --- a/backend/src/services/org/org-types.ts +++ b/backend/src/services/org/org-types.ts @@ -1,4 +1,3 @@ -import { ProjectType } from "@app/db/schemas"; import { TOrgPermission } from "@app/lib/types"; import { ActorAuthMethod, ActorType, MfaMethod } from "../auth/auth-type"; @@ -60,7 +59,6 @@ export type TFindAllWorkspacesDTO = { actorOrgId: string | undefined; actorAuthMethod: ActorAuthMethod; orgId: string; - type?: ProjectType; }; export type TUpdateOrgDTO = { diff --git a/backend/src/services/pki-alert/pki-alert-service.ts b/backend/src/services/pki-alert/pki-alert-service.ts index 946740b66..8b348085f 100644 --- a/backend/src/services/pki-alert/pki-alert-service.ts +++ b/backend/src/services/pki-alert/pki-alert-service.ts @@ -1,6 +1,5 @@ import { ForbiddenError } from "@casl/ability"; -import { ActionProjectType, ProjectType } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { ForbiddenRequestError, NotFoundError } from "@app/lib/errors"; @@ -9,7 +8,6 @@ import { TPkiCollectionDALFactory } from "@app/services/pki-collection/pki-colle import { pkiItemTypeToNameMap } from "@app/services/pki-collection/pki-collection-types"; import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service"; -import { TProjectDALFactory } from "../project/project-dal"; import { TPkiAlertDALFactory } from "./pki-alert-dal"; import { TCreateAlertDTO, TDeleteAlertDTO, TGetAlertByIdDTO, TUpdateAlertDTO } from "./pki-alert-types"; @@ -21,7 +19,6 @@ type TPkiAlertServiceFactoryDep = { pkiCollectionDAL: Pick; permissionService: Pick; smtpService: Pick; - projectDAL: Pick; }; export type TPkiAlertServiceFactory = ReturnType; @@ -30,8 +27,7 @@ export const pkiAlertServiceFactory = ({ pkiAlertDAL, pkiCollectionDAL, permissionService, - smtpService, - projectDAL + smtpService }: TPkiAlertServiceFactoryDep) => { const sendPkiItemExpiryNotices = async () => { const allAlertItems = await pkiAlertDAL.getExpiringPkiCollectionItemsForAlerting(); @@ -67,7 +63,7 @@ export const pkiAlertServiceFactory = ({ }; const createPkiAlert = async ({ - projectId: preSplitProjectId, + projectId, name, pkiCollectionId, alertBeforeDays, @@ -77,22 +73,12 @@ export const pkiAlertServiceFactory = ({ actor, actorOrgId }: TCreateAlertDTO) => { - let projectId = preSplitProjectId; - const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId( - projectId, - ProjectType.CertificateManager - ); - if (certManagerProjectFromSplit) { - projectId = certManagerProjectFromSplit.id; - } - const { permission } = await permissionService.getProjectPermission({ actor, actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.PkiAlerts); @@ -121,8 +107,7 @@ export const pkiAlertServiceFactory = ({ actorId, projectId: alert.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.PkiAlerts); @@ -148,8 +133,7 @@ export const pkiAlertServiceFactory = ({ actorId, projectId: alert.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.PkiAlerts); @@ -181,8 +165,7 @@ export const pkiAlertServiceFactory = ({ actorId, projectId: alert.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.PkiAlerts); diff --git a/backend/src/services/pki-collection/pki-collection-service.ts b/backend/src/services/pki-collection/pki-collection-service.ts index 8d758b5e9..7c89ce255 100644 --- a/backend/src/services/pki-collection/pki-collection-service.ts +++ b/backend/src/services/pki-collection/pki-collection-service.ts @@ -1,13 +1,12 @@ import { ForbiddenError } from "@casl/ability"; -import { ActionProjectType, ProjectType, TPkiCollectionItems } from "@app/db/schemas"; +import { TPkiCollectionItems } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { BadRequestError, NotFoundError } from "@app/lib/errors"; import { TCertificateDALFactory } from "@app/services/certificate/certificate-dal"; import { TCertificateAuthorityDALFactory } from "@app/services/certificate-authority/certificate-authority-dal"; -import { TProjectDALFactory } from "../project/project-dal"; import { TPkiCollectionDALFactory } from "./pki-collection-dal"; import { transformPkiCollectionItem } from "./pki-collection-fns"; import { TPkiCollectionItemDALFactory } from "./pki-collection-item-dal"; @@ -31,7 +30,6 @@ type TPkiCollectionServiceFactoryDep = { certificateAuthorityDAL: Pick; certificateDAL: Pick; permissionService: Pick; - projectDAL: Pick; }; export type TPkiCollectionServiceFactory = ReturnType; @@ -41,34 +39,23 @@ export const pkiCollectionServiceFactory = ({ pkiCollectionItemDAL, certificateAuthorityDAL, certificateDAL, - permissionService, - projectDAL + permissionService }: TPkiCollectionServiceFactoryDep) => { const createPkiCollection = async ({ name, description, - projectId: preSplitProjectId, + projectId, actorId, actorAuthMethod, actor, actorOrgId }: TCreatePkiCollectionDTO) => { - let projectId = preSplitProjectId; - const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId( - projectId, - ProjectType.CertificateManager - ); - if (certManagerProjectFromSplit) { - projectId = certManagerProjectFromSplit.id; - } - const { permission } = await permissionService.getProjectPermission({ actor, actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -100,8 +87,7 @@ export const pkiCollectionServiceFactory = ({ actorId, projectId: pkiCollection.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.PkiCollections); @@ -125,8 +111,7 @@ export const pkiCollectionServiceFactory = ({ actorId, projectId: pkiCollection.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.PkiCollections); @@ -153,8 +138,7 @@ export const pkiCollectionServiceFactory = ({ actorId, projectId: pkiCollection.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -183,8 +167,7 @@ export const pkiCollectionServiceFactory = ({ actorId, projectId: pkiCollection.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.PkiCollections); @@ -227,8 +210,7 @@ export const pkiCollectionServiceFactory = ({ actorId, projectId: pkiCollection.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -315,8 +297,7 @@ export const pkiCollectionServiceFactory = ({ actorId, projectId: pkiCollection.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( diff --git a/backend/src/services/pki-subscriber/pki-subscriber-service.ts b/backend/src/services/pki-subscriber/pki-subscriber-service.ts index a3e6ec78c..245337296 100644 --- a/backend/src/services/pki-subscriber/pki-subscriber-service.ts +++ b/backend/src/services/pki-subscriber/pki-subscriber-service.ts @@ -2,7 +2,6 @@ import { ForbiddenError, subject } from "@casl/ability"; import * as x509 from "@peculiar/x509"; -import { ActionProjectType } from "@app/db/schemas"; import { TCertificateAuthorityCrlDALFactory } from "@app/ee/services/certificate-authority-crl/certificate-authority-crl-dal"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { @@ -120,8 +119,7 @@ export const pkiSubscriberServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -183,8 +181,7 @@ export const pkiSubscriberServiceFactory = ({ actorId, projectId: subscriber.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -237,8 +234,7 @@ export const pkiSubscriberServiceFactory = ({ actorId, projectId: subscriber.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -300,8 +296,7 @@ export const pkiSubscriberServiceFactory = ({ actorId, projectId: subscriber.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -337,8 +332,7 @@ export const pkiSubscriberServiceFactory = ({ actorId, projectId: subscriber.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -393,8 +387,7 @@ export const pkiSubscriberServiceFactory = ({ actorId, projectId: subscriber.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -440,8 +433,7 @@ export const pkiSubscriberServiceFactory = ({ actorId, projectId: ca.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -699,8 +691,7 @@ export const pkiSubscriberServiceFactory = ({ actorId, projectId: subscriber.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -747,8 +738,7 @@ export const pkiSubscriberServiceFactory = ({ actorId, projectId: subscriber.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( diff --git a/backend/src/services/pki-templates/pki-templates-service.ts b/backend/src/services/pki-templates/pki-templates-service.ts index e648ab88f..98469c157 100644 --- a/backend/src/services/pki-templates/pki-templates-service.ts +++ b/backend/src/services/pki-templates/pki-templates-service.ts @@ -3,7 +3,6 @@ import { ForbiddenError, subject } from "@casl/ability"; import * as x509 from "@peculiar/x509"; import RE2 from "re2"; -import { ActionProjectType } from "@app/db/schemas"; import { TCertificateAuthorityCrlDALFactory } from "@app/ee/services/certificate-authority-crl/certificate-authority-crl-dal"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { @@ -119,8 +118,7 @@ export const pkiTemplatesServiceFactory = ({ actorId, projectId: ca.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -172,8 +170,7 @@ export const pkiTemplatesServiceFactory = ({ actorId, projectId: certTemplate.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -236,8 +233,7 @@ export const pkiTemplatesServiceFactory = ({ actorId, projectId: certTemplate.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -269,8 +265,7 @@ export const pkiTemplatesServiceFactory = ({ actorId, projectId: certTemplate.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -295,8 +290,7 @@ export const pkiTemplatesServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); const certTemplate = await pkiTemplatesDAL.find({ projectId }, { limit, offset, count: true }); @@ -338,8 +332,7 @@ export const pkiTemplatesServiceFactory = ({ actorId, projectId: certTemplate.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -385,8 +378,7 @@ export const pkiTemplatesServiceFactory = ({ actorId, projectId: certTemplate.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( diff --git a/backend/src/services/project-bot/project-bot-service.ts b/backend/src/services/project-bot/project-bot-service.ts index a5a7d47b3..12e4ecbdc 100644 --- a/backend/src/services/project-bot/project-bot-service.ts +++ b/backend/src/services/project-bot/project-bot-service.ts @@ -1,6 +1,6 @@ import { ForbiddenError } from "@casl/ability"; -import { ActionProjectType, ProjectVersion } from "@app/db/schemas"; +import { ProjectVersion } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { crypto } from "@app/lib/crypto/cryptography"; @@ -45,8 +45,7 @@ export const projectBotServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); @@ -115,8 +114,7 @@ export const projectBotServiceFactory = ({ actorId, projectId: bot.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations); diff --git a/backend/src/services/project-env/project-env-service.ts b/backend/src/services/project-env/project-env-service.ts index 9a82a6bbe..6773ee600 100644 --- a/backend/src/services/project-env/project-env-service.ts +++ b/backend/src/services/project-env/project-env-service.ts @@ -1,6 +1,5 @@ import { ForbiddenError } from "@casl/ability"; -import { ActionProjectType } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; @@ -47,8 +46,7 @@ export const projectEnvServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Environments); @@ -136,8 +134,7 @@ export const projectEnvServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Environments); @@ -200,8 +197,7 @@ export const projectEnvServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Environments); @@ -256,8 +252,7 @@ export const projectEnvServiceFactory = ({ actorId, projectId: environment.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Environments); diff --git a/backend/src/services/project-key/project-key-service.ts b/backend/src/services/project-key/project-key-service.ts index a884d25bc..c4eae9e2e 100644 --- a/backend/src/services/project-key/project-key-service.ts +++ b/backend/src/services/project-key/project-key-service.ts @@ -1,6 +1,5 @@ import { ForbiddenError } from "@casl/ability"; -import { ActionProjectType } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionMemberActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { BadRequestError } from "@app/lib/errors"; @@ -37,8 +36,7 @@ export const projectKeyServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Edit, ProjectPermissionSub.Member); @@ -67,8 +65,7 @@ export const projectKeyServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); const latestKey = await projectKeyDAL.findLatestProjectKey(actorId, projectId); return latestKey; @@ -86,8 +83,7 @@ export const projectKeyServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Read, ProjectPermissionSub.Member); return projectKeyDAL.findAllProjectUserPubKeys(projectId); diff --git a/backend/src/services/project-membership/project-membership-service.ts b/backend/src/services/project-membership/project-membership-service.ts index b9e502922..9cef4dabf 100644 --- a/backend/src/services/project-membership/project-membership-service.ts +++ b/backend/src/services/project-membership/project-membership-service.ts @@ -1,7 +1,7 @@ /* eslint-disable no-await-in-loop */ import { ForbiddenError } from "@casl/ability"; -import { ActionProjectType, ProjectMembershipRole, ProjectVersion, TableName } from "@app/db/schemas"; +import { ProjectMembershipRole, ProjectVersion, TableName } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { constructPermissionErrorMessage, @@ -90,8 +90,7 @@ export const projectMembershipServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Read, ProjectPermissionSub.Member); @@ -134,8 +133,7 @@ export const projectMembershipServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Read, ProjectPermissionSub.Member); @@ -157,8 +155,7 @@ export const projectMembershipServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Read, ProjectPermissionSub.Member); @@ -184,8 +181,7 @@ export const projectMembershipServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Create, ProjectPermissionSub.Member); const orgMembers = await orgDAL.findMembership({ @@ -265,8 +261,7 @@ export const projectMembershipServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Edit, ProjectPermissionSub.Member); @@ -375,8 +370,7 @@ export const projectMembershipServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Delete, ProjectPermissionSub.Member); @@ -418,8 +412,7 @@ export const projectMembershipServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Delete, ProjectPermissionSub.Member); diff --git a/backend/src/services/project-role/project-role-fns.ts b/backend/src/services/project-role/project-role-fns.ts index 4dfcf960b..bf5044f47 100644 --- a/backend/src/services/project-role/project-role-fns.ts +++ b/backend/src/services/project-role/project-role-fns.ts @@ -11,7 +11,7 @@ import { } from "@app/ee/services/permission/default-roles"; import { TGetPredefinedRolesDTO } from "@app/services/project-role/project-role-types"; -export const getPredefinedRoles = ({ projectId, projectType, roleFilter }: TGetPredefinedRolesDTO) => { +export const getPredefinedRoles = ({ projectId, roleFilter }: TGetPredefinedRolesDTO) => { return [ { id: uuidv4(), @@ -75,5 +75,5 @@ export const getPredefinedRoles = ({ projectId, projectType, roleFilter }: TGetP createdAt: new Date(), updatedAt: new Date() } - ].filter(({ slug, type }) => (type ? type === projectType : true) && (!roleFilter || roleFilter === slug)); + ].filter(({ slug }) => !roleFilter || roleFilter === slug); }; diff --git a/backend/src/services/project-role/project-role-service.ts b/backend/src/services/project-role/project-role-service.ts index dd0eecc68..76613805e 100644 --- a/backend/src/services/project-role/project-role-service.ts +++ b/backend/src/services/project-role/project-role-service.ts @@ -2,7 +2,7 @@ import { ForbiddenError, MongoAbility, RawRuleOf } from "@casl/ability"; import { PackRule, packRules, unpackRules } from "@casl/ability/extra"; import { requestContext } from "@fastify/request-context"; -import { ActionProjectType, ProjectMembershipRole, ProjectType, TableName, TProjects } from "@app/db/schemas"; +import { ProjectMembershipRole, TableName, TProjects } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionActions, @@ -71,8 +71,7 @@ export const projectRoleServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Role); const existingRole = await projectRoleDAL.findOne({ slug: data.slug, projectId }); @@ -112,14 +111,12 @@ export const projectRoleServiceFactory = ({ actorId, projectId: project.id, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Role); if (roleSlug !== "custom" && Object.values(ProjectMembershipRole).includes(roleSlug as ProjectMembershipRole)) { const [predefinedRole] = getPredefinedRoles({ projectId: project.id, - projectType: project.type as ProjectType, roleFilter: roleSlug as ProjectMembershipRole }); @@ -142,8 +139,7 @@ export const projectRoleServiceFactory = ({ actorId, projectId: projectRole.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Role); @@ -173,8 +169,7 @@ export const projectRoleServiceFactory = ({ actorId, projectId: projectRole.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Role); @@ -215,18 +210,14 @@ export const projectRoleServiceFactory = ({ actorId, projectId: project.id, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Role); const customRoles = await projectRoleDAL.find( { projectId: project.id }, { sort: [[`${TableName.ProjectRoles}.slug` as "slug", "asc"]] } ); - const roles = [ - ...getPredefinedRoles({ projectId: project.id, projectType: project.type as ProjectType }), - ...(customRoles || []) - ]; + const roles = [...getPredefinedRoles({ projectId: project.id }), ...(customRoles || [])]; return roles; }; @@ -242,8 +233,7 @@ export const projectRoleServiceFactory = ({ actorId: userId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); // just to satisfy ts if (!("roles" in membership)) throw new BadRequestError({ message: "Service token not allowed" }); diff --git a/backend/src/services/project-role/project-role-types.ts b/backend/src/services/project-role/project-role-types.ts index 508623a0c..37395a9a7 100644 --- a/backend/src/services/project-role/project-role-types.ts +++ b/backend/src/services/project-role/project-role-types.ts @@ -1,4 +1,4 @@ -import { ProjectMembershipRole, ProjectType, TOrgRolesUpdate, TProjectRolesInsert } from "@app/db/schemas"; +import { ProjectMembershipRole, TOrgRolesUpdate, TProjectRolesInsert } from "@app/db/schemas"; import { TProjectPermission } from "@app/lib/types"; export enum ProjectRoleServiceIdentifierType { @@ -37,6 +37,5 @@ export type TListRolesDTO = { export type TGetPredefinedRolesDTO = { projectId: string; - projectType: ProjectType; roleFilter?: ProjectMembershipRole; }; diff --git a/backend/src/services/project/project-dal.ts b/backend/src/services/project/project-dal.ts index 7f733503c..bd008a5be 100644 --- a/backend/src/services/project/project-dal.ts +++ b/backend/src/services/project/project-dal.ts @@ -3,7 +3,6 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; import { ProjectsSchema, - ProjectType, ProjectUpgradeStatus, ProjectVersion, SortDirection, @@ -22,17 +21,12 @@ export type TProjectDALFactory = ReturnType; export const projectDALFactory = (db: TDbClient) => { const projectOrm = ormify(db, TableName.Project); - const findIdentityProjects = async (identityId: string, orgId: string, projectType: ProjectType | "all") => { + const findIdentityProjects = async (identityId: string, orgId: string) => { try { const workspaces = await db(TableName.IdentityProjectMembership) .where({ identityId }) .join(TableName.Project, `${TableName.IdentityProjectMembership}.projectId`, `${TableName.Project}.id`) .where(`${TableName.Project}.orgId`, orgId) - .andWhere((qb) => { - if (projectType !== "all") { - void qb.where(`${TableName.Project}.type`, projectType); - } - }) .leftJoin(TableName.Environment, `${TableName.Environment}.projectId`, `${TableName.Project}.id`) .select( selectAllTableCols(TableName.Project), @@ -72,18 +66,13 @@ export const projectDALFactory = (db: TDbClient) => { } }; - const findUserProjects = async (userId: string, orgId: string, projectType: ProjectType | "all") => { + const findUserProjects = async (userId: string, orgId: string) => { try { const workspaces = await db .replicaNode()(TableName.ProjectMembership) .where({ userId }) .join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`) .where(`${TableName.Project}.orgId`, orgId) - .andWhere((qb) => { - if (projectType !== "all") { - void qb.where(`${TableName.Project}.type`, projectType); - } - }) .leftJoin(TableName.Environment, `${TableName.Environment}.projectId`, `${TableName.Project}.id`) .select( selectAllTableCols(TableName.Project), @@ -103,11 +92,6 @@ export const projectDALFactory = (db: TDbClient) => { .whereIn("groupId", groups) .join(TableName.Project, `${TableName.GroupProjectMembership}.projectId`, `${TableName.Project}.id`) .where(`${TableName.Project}.orgId`, orgId) - .andWhere((qb) => { - if (projectType !== "all") { - void qb.where(`${TableName.Project}.type`, projectType); - } - }) .whereNotIn( `${TableName.Project}.id`, workspaces.map(({ id }) => id) @@ -177,17 +161,12 @@ export const projectDALFactory = (db: TDbClient) => { } }; - const findAllProjectsByIdentity = async (identityId: string, projectType?: ProjectType) => { + const findAllProjectsByIdentity = async (identityId: string) => { try { const workspaces = await db .replicaNode()(TableName.IdentityProjectMembership) .where({ identityId }) .join(TableName.Project, `${TableName.IdentityProjectMembership}.projectId`, `${TableName.Project}.id`) - .andWhere((qb) => { - if (projectType) { - void qb.where(`${TableName.Project}.type`, projectType); - } - }) .leftJoin(TableName.Environment, `${TableName.Environment}.projectId`, `${TableName.Project}.id`) .select( selectAllTableCols(TableName.Project), @@ -389,27 +368,10 @@ export const projectDALFactory = (db: TDbClient) => { }; }; - const getProjectFromSplitId = async (projectId: string, projectType: ProjectType) => { - try { - const project = await db(TableName.ProjectSplitBackfillIds) - .where({ - sourceProjectId: projectId, - destinationProjectType: projectType - }) - .join(TableName.Project, `${TableName.Project}.id`, `${TableName.ProjectSplitBackfillIds}.destinationProjectId`) - .select(selectAllTableCols(TableName.Project)) - .first(); - return project; - } catch (error) { - throw new DatabaseError({ error, name: `Failed to find split project with id ${projectId}` }); - } - }; - const searchProjects = async (dto: { orgId: string; actor: ActorType; actorId: string; - type?: ProjectType; limit?: number; offset?: number; name?: string; @@ -464,9 +426,6 @@ export const projectDALFactory = (db: TDbClient) => { void query.orderBy([{ column: `${TableName.Project}.name`, order: sortDir }]); } - if (dto.type) { - void query.where(`${TableName.Project}.type`, dto.type); - } if (dto.name) { void query.whereILike(`${TableName.Project}.name`, `%${dto.name}%`); } @@ -512,7 +471,6 @@ export const projectDALFactory = (db: TDbClient) => { findProjectBySlug, findProjectWithOrg, checkProjectUpgradeStatus, - getProjectFromSplitId, searchProjects, findProjectByEnvId, countOfOrgProjects diff --git a/backend/src/services/project/project-fns.ts b/backend/src/services/project/project-fns.ts index 306cba50a..f166ec04f 100644 --- a/backend/src/services/project/project-fns.ts +++ b/backend/src/services/project/project-fns.ts @@ -137,7 +137,7 @@ export const bootstrapSshProject = async ({ tx }); - await projectSshConfigDAL.create( + const sshConfig = await projectSshConfigDAL.create( { projectId, defaultHostSshCaId: hostSshCa.id, @@ -145,4 +145,5 @@ export const bootstrapSshProject = async ({ }, tx ); + return sshConfig; }; diff --git a/backend/src/services/project/project-service.ts b/backend/src/services/project/project-service.ts index b649d8e4c..cff47ff29 100644 --- a/backend/src/services/project/project-service.ts +++ b/backend/src/services/project/project-service.ts @@ -1,14 +1,7 @@ import { ForbiddenError, subject } from "@casl/ability"; import slugify from "@sindresorhus/slugify"; -import { - ActionProjectType, - ProjectMembershipRole, - ProjectType, - ProjectVersion, - TableName, - TProjectEnvironments -} from "@app/db/schemas"; +import { ProjectMembershipRole, ProjectVersion, TableName, TProjectEnvironments } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { throwIfMissingSecretReadValueOrDescribePermission } from "@app/ee/services/permission/permission-fns"; @@ -249,8 +242,7 @@ export const projectServiceFactory = ({ kmsKeyId, tx: trx, createDefaultEnvs = true, - template = InfisicalProjectTemplate.Default, - type = ProjectType.SecretManager + template = InfisicalProjectTemplate.Default }: TCreateProjectDTO) => { const organization = await orgDAL.findOne({ id: actorOrgId }); const { permission, membership: orgMembership } = await permissionService.getOrgPermission( @@ -266,11 +258,7 @@ export const projectServiceFactory = ({ await tx.raw("SELECT pg_advisory_xact_lock(?)", [PgSqlLock.CreateProject(organization.id)]); const plan = await licenseService.getPlan(organization.id); - if ( - plan.workspaceLimit !== null && - plan.workspacesUsed >= plan.workspaceLimit && - type === ProjectType.SecretManager - ) { + if (plan.workspaceLimit !== null && plan.workspacesUsed >= plan.workspaceLimit) { // case: limit imposed on number of workspaces allowed // case: number of workspaces used exceeds the number of workspaces allowed throw new BadRequestError({ @@ -307,7 +295,6 @@ export const projectServiceFactory = ({ const project = await projectDAL.create( { name: workspaceName, - type, description: workspaceDescription, orgId: organization.id, slug: projectSlug || slugify(`${workspaceName}-${alphaNumericNanoId(4)}`), @@ -318,16 +305,14 @@ export const projectServiceFactory = ({ tx ); - if (type === ProjectType.SSH) { - await bootstrapSshProject({ - projectId: project.id, - sshCertificateAuthorityDAL, - sshCertificateAuthoritySecretDAL, - kmsService, - projectSshConfigDAL, - tx - }); - } + await bootstrapSshProject({ + projectId: project.id, + sshCertificateAuthorityDAL, + sshCertificateAuthoritySecretDAL, + kmsService, + projectSshConfigDAL, + tx + }); // set ghost user as admin of project const projectMembership = await projectMembershipDAL.create( @@ -526,8 +511,7 @@ export const projectServiceFactory = ({ actorId, projectId: project.id, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Project); @@ -585,18 +569,11 @@ export const projectServiceFactory = ({ return deletedProject; }; - const getProjects = async ({ - actorId, - actor, - includeRoles, - actorAuthMethod, - actorOrgId, - type = ProjectType.SecretManager - }: TListProjectsDTO) => { + const getProjects = async ({ actorId, actor, includeRoles, actorAuthMethod, actorOrgId }: TListProjectsDTO) => { const workspaces = actor === ActorType.IDENTITY - ? await projectDAL.findIdentityProjects(actorId, actorOrgId, type) - : await projectDAL.findUserProjects(actorId, actorOrgId, type); + ? await projectDAL.findIdentityProjects(actorId, actorOrgId) + : await projectDAL.findUserProjects(actorId, actorOrgId); if (includeRoles) { const { permission } = await permissionService.getUserOrgPermission( @@ -620,10 +597,7 @@ export const projectServiceFactory = ({ workspaces.map(async (workspace) => { return { ...workspace, - roles: [ - ...(workspaceMappedToRoles[workspace.id] || []), - ...getPredefinedRoles({ projectId: workspace.id, projectType: workspace.type as ProjectType }) - ] + roles: [...(workspaceMappedToRoles[workspace.id] || []), ...getPredefinedRoles({ projectId: workspace.id })] }; }) ); @@ -642,8 +616,7 @@ export const projectServiceFactory = ({ actorId, projectId: project.id, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); return project; }; @@ -656,8 +629,7 @@ export const projectServiceFactory = ({ actorId, projectId: project.id, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings); @@ -681,6 +653,7 @@ export const projectServiceFactory = ({ hasDeleteProtection: update.hasDeleteProtection, slug: update.slug, secretSharing: update.secretSharing, + defaultProduct: update.defaultProduct, showSnapshotsLegacy: update.showSnapshotsLegacy }); @@ -700,8 +673,7 @@ export const projectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings); @@ -726,8 +698,7 @@ export const projectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings); @@ -756,8 +727,7 @@ export const projectServiceFactory = ({ actorId, projectId: project.id, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); if (!hasRole(ProjectMembershipRole.Admin)) @@ -788,8 +758,7 @@ export const projectServiceFactory = ({ actorId, projectId: project.id, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); if (!hasRole(ProjectMembershipRole.Admin)) { @@ -821,8 +790,7 @@ export const projectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings); @@ -843,8 +811,7 @@ export const projectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Project); @@ -914,8 +881,7 @@ export const projectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); throwIfMissingSecretReadValueOrDescribePermission(permission, ProjectPermissionSecretActions.DescribeSecret); @@ -946,22 +912,14 @@ export const projectServiceFactory = ({ actor }: TListProjectCasDTO) => { const project = await projectDAL.findProjectByFilter(filter); - let projectId = project.id; - const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId( - projectId, - ProjectType.CertificateManager - ); - if (certManagerProjectFromSplit) { - projectId = certManagerProjectFromSplit.id; - } + const projectId = project.id; const { permission } = await permissionService.getProjectPermission({ actor, actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -1000,22 +958,14 @@ export const projectServiceFactory = ({ actor }: TListProjectCertsDTO) => { const project = await projectDAL.findProjectByFilter(filter); - let projectId = project.id; - const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId( - projectId, - ProjectType.CertificateManager - ); - if (certManagerProjectFromSplit) { - projectId = certManagerProjectFromSplit.id; - } + const projectId = project.id; const { permission } = await permissionService.getProjectPermission({ actor, actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -1048,28 +998,18 @@ export const projectServiceFactory = ({ * Return list of (PKI) alerts configured for project */ const listProjectAlerts = async ({ - projectId: preSplitProjectId, + projectId, actor, actorId, actorAuthMethod, actorOrgId }: TListProjectAlertsDTO) => { - let projectId = preSplitProjectId; - const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId( - projectId, - ProjectType.CertificateManager - ); - if (certManagerProjectFromSplit) { - projectId = certManagerProjectFromSplit.id; - } - const { permission } = await permissionService.getProjectPermission({ actor, actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.PkiAlerts); @@ -1085,27 +1025,18 @@ export const projectServiceFactory = ({ * Return list of PKI collections for project */ const listProjectPkiCollections = async ({ - projectId: preSplitProjectId, + projectId, actor, actorId, actorAuthMethod, actorOrgId }: TListProjectAlertsDTO) => { - let projectId = preSplitProjectId; - const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId( - projectId, - ProjectType.CertificateManager - ); - if (certManagerProjectFromSplit) { - projectId = certManagerProjectFromSplit.id; - } const { permission } = await permissionService.getProjectPermission({ actor, actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.PkiCollections); @@ -1132,8 +1063,7 @@ export const projectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); const allowedSubscribers = []; @@ -1160,28 +1090,18 @@ export const projectServiceFactory = ({ * Return list of certificate templates for project */ const listProjectCertificateTemplates = async ({ - projectId: preSplitProjectId, + projectId, actorId, actorOrgId, actorAuthMethod, actor }: TListProjectCertificateTemplatesDTO) => { - let projectId = preSplitProjectId; - const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId( - projectId, - ProjectType.CertificateManager - ); - if (certManagerProjectFromSplit) { - projectId = certManagerProjectFromSplit.id; - } - const { permission } = await permissionService.getProjectPermission({ actor, actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.CertificateManager + actorOrgId }); const certificateTemplates = await certificateTemplateDAL.getCertTemplatesByProjectId(projectId); @@ -1211,8 +1131,7 @@ export const projectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -1245,8 +1164,7 @@ export const projectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); const allowedHosts = []; @@ -1285,8 +1203,7 @@ export const projectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SshHostGroups); @@ -1313,8 +1230,7 @@ export const projectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificates); @@ -1352,8 +1268,7 @@ export const projectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -1387,8 +1302,7 @@ export const projectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Kms); @@ -1415,8 +1329,7 @@ export const projectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Kms); @@ -1445,8 +1358,7 @@ export const projectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Kms); @@ -1468,8 +1380,7 @@ export const projectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); if (!membership) { @@ -1501,8 +1412,7 @@ export const projectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Settings); @@ -1541,8 +1451,7 @@ export const projectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SSH + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings); @@ -1625,8 +1534,7 @@ export const projectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Settings); @@ -1698,8 +1606,7 @@ export const projectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings); @@ -1776,8 +1683,7 @@ export const projectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings); @@ -1900,8 +1806,7 @@ export const projectServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Settings); @@ -1929,15 +1834,7 @@ export const projectServiceFactory = ({ }); }; - const searchProjects = async ({ - name, - offset, - permission, - limit, - type, - orderBy, - orderDirection - }: TSearchProjectsDTO) => { + const searchProjects = async ({ name, offset, permission, limit, orderBy, orderDirection }: TSearchProjectsDTO) => { // check user belong to org await permissionService.getOrgPermission( permission.type, @@ -1951,7 +1848,6 @@ export const projectServiceFactory = ({ limit, offset, name, - type, orgId: permission.orgId, actor: permission.type, actorId: permission.id, @@ -1975,7 +1871,7 @@ export const projectServiceFactory = ({ actor: permission.type, actorId: permission.id, projectId, - actionProjectType: ActionProjectType.Any, + actorAuthMethod: permission.authMethod, actorOrgId: permission.orgId }) diff --git a/backend/src/services/project/project-types.ts b/backend/src/services/project/project-types.ts index 8ef72492a..5d4578194 100644 --- a/backend/src/services/project/project-types.ts +++ b/backend/src/services/project/project-types.ts @@ -92,6 +92,7 @@ export type TUpdateProjectDTO = { description?: string; autoCapitalization?: boolean; hasDeleteProtection?: boolean; + defaultProduct?: ProjectType; slug?: string; secretSharing?: boolean; showSnapshotsLegacy?: boolean; diff --git a/backend/src/services/resource-cleanup/resource-cleanup-queue.ts b/backend/src/services/resource-cleanup/resource-cleanup-queue.ts index 32f180636..39926488f 100644 --- a/backend/src/services/resource-cleanup/resource-cleanup-queue.ts +++ b/backend/src/services/resource-cleanup/resource-cleanup-queue.ts @@ -5,6 +5,7 @@ import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue"; import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal"; import { TIdentityUaClientSecretDALFactory } from "../identity-ua/identity-ua-client-secret-dal"; +import { TOrgServiceFactory } from "../org/org-service"; import { TSecretDALFactory } from "../secret/secret-dal"; import { TSecretVersionDALFactory } from "../secret/secret-version-dal"; import { TSecretFolderVersionDALFactory } from "../secret-folder/secret-folder-version-dal"; @@ -24,6 +25,7 @@ type TDailyResourceCleanUpQueueServiceFactoryDep = { secretSharingDAL: Pick; serviceTokenService: Pick; queueService: TQueueServiceFactory; + orgService: TOrgServiceFactory; }; export type TDailyResourceCleanUpQueueServiceFactory = ReturnType; @@ -39,12 +41,12 @@ export const dailyResourceCleanUpQueueServiceFactory = ({ secretSharingDAL, secretVersionV2DAL, identityUniversalAuthClientSecretDAL, - serviceTokenService + serviceTokenService, + orgService }: TDailyResourceCleanUpQueueServiceFactoryDep) => { queueService.start(QueueName.DailyResourceCleanUp, async () => { logger.info(`${QueueName.DailyResourceCleanUp}: queue task started`); await secretDAL.pruneSecretReminders(queueService); - await auditLogDAL.pruneAuditLog(); await identityAccessTokenDAL.removeExpiredTokens(); await identityUniversalAuthClientSecretDAL.removeExpiredClientSecrets(); await secretSharingDAL.pruneExpiredSharedSecrets(); @@ -54,6 +56,8 @@ export const dailyResourceCleanUpQueueServiceFactory = ({ await secretVersionV2DAL.pruneExcessVersions(); await secretFolderVersionDAL.pruneExcessVersions(); await serviceTokenService.notifyExpiringTokens(); + await orgService.notifyInvitedUsers(); + await auditLogDAL.pruneAuditLog(); logger.info(`${QueueName.DailyResourceCleanUp}: queue task completed`); }); diff --git a/backend/src/services/secret-blind-index/secret-blind-index-service.ts b/backend/src/services/secret-blind-index/secret-blind-index-service.ts index a19ce8b88..c8fed2a2b 100644 --- a/backend/src/services/secret-blind-index/secret-blind-index-service.ts +++ b/backend/src/services/secret-blind-index/secret-blind-index-service.ts @@ -1,4 +1,4 @@ -import { ActionProjectType, ProjectMembershipRole } from "@app/db/schemas"; +import { ProjectMembershipRole } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ForbiddenRequestError, NotFoundError } from "@app/lib/errors"; @@ -36,8 +36,7 @@ export const secretBlindIndexServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const secretCount = await secretBlindIndexDAL.countOfSecretsWithNullSecretBlindIndex(projectId); @@ -56,8 +55,7 @@ export const secretBlindIndexServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); if (!hasRole(ProjectMembershipRole.Admin)) { throw new ForbiddenRequestError({ message: "Insufficient privileges, user must be admin" }); @@ -80,8 +78,7 @@ export const secretBlindIndexServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); if (!hasRole(ProjectMembershipRole.Admin)) { throw new ForbiddenRequestError({ message: "Insufficient privileges, user must be admin" }); diff --git a/backend/src/services/secret-folder/secret-folder-service.ts b/backend/src/services/secret-folder/secret-folder-service.ts index da29c0f36..e06cfcb01 100644 --- a/backend/src/services/secret-folder/secret-folder-service.ts +++ b/backend/src/services/secret-folder/secret-folder-service.ts @@ -2,9 +2,10 @@ import { ForbiddenError, subject } from "@casl/ability"; import path from "path"; import { v4 as uuidv4, validate as uuidValidate } from "uuid"; -import { ActionProjectType, TSecretFoldersInsert } from "@app/db/schemas"; +import { TProjectEnvironments, TSecretFolders, TSecretFoldersInsert } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; +import { TSecretApprovalPolicyServiceFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-service"; import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service"; import { PgSqlLock } from "@app/keystore/keystore"; import { BadRequestError, NotFoundError } from "@app/lib/errors"; @@ -14,6 +15,7 @@ import { buildFolderPath } from "@app/services/secret-folder/secret-folder-fns"; import { ChangeType, CommitType, TFolderCommitServiceFactory } from "../folder-commit/folder-commit-service"; import { TProjectDALFactory } from "../project/project-dal"; import { TProjectEnvDALFactory } from "../project-env/project-env-dal"; +import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal"; import { TSecretFolderDALFactory } from "./secret-folder-dal"; import { TCreateFolderDTO, @@ -34,6 +36,8 @@ type TSecretFolderServiceFactoryDep = { folderVersionDAL: Pick; folderCommitService: Pick; projectDAL: Pick; + secretApprovalPolicyService: Pick; + secretV2BridgeDAL: Pick; }; export type TSecretFolderServiceFactory = ReturnType; @@ -45,7 +49,9 @@ export const secretFolderServiceFactory = ({ projectEnvDAL, folderVersionDAL, folderCommitService, - projectDAL + projectDAL, + secretApprovalPolicyService, + secretV2BridgeDAL }: TSecretFolderServiceFactoryDep) => { const createFolder = async ({ projectId, @@ -63,8 +69,7 @@ export const secretFolderServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -245,8 +250,7 @@ export const secretFolderServiceFactory = ({ actorId, projectId: project.id, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); folders.forEach(({ environment, path: secretPath }) => { @@ -377,8 +381,7 @@ export const secretFolderServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -464,6 +467,106 @@ export const secretFolderServiceFactory = ({ return { folder: newFolder, old: folder }; }; + const $checkFolderPolicy = async ({ + projectId, + env, + parentId, + idOrName + }: { + projectId: string; + env: TProjectEnvironments; + parentId: string; + idOrName: string; + }) => { + let targetFolder = await folderDAL + .findOne({ + envId: env.id, + name: idOrName, + parentId, + isReserved: false + }) + .catch(() => null); + + if (!targetFolder && uuidValidate(idOrName)) { + targetFolder = await folderDAL + .findOne({ + envId: env.id, + id: idOrName, + parentId, + isReserved: false + }) + .catch(() => null); + } + + if (!targetFolder) { + throw new NotFoundError({ message: `Target folder not found` }); + } + + // get environment root folder (as it's needed to get all folders under it) + const rootFolder = await folderDAL.findBySecretPath(projectId, env.slug, "/"); + if (!rootFolder) throw new NotFoundError({ message: `Root folder not found` }); + // get all folders under environment root folder + const folderPaths = await folderDAL.findByEnvsDeep({ parentIds: [rootFolder.id] }); + + // create a map of folders by parent id + const normalizeKey = (key: string | null | undefined): string => key ?? "root"; + const folderMap = new Map(); + for (const folder of folderPaths) { + if (!folderMap.has(normalizeKey(folder.parentId))) { + folderMap.set(normalizeKey(folder.parentId), []); + } + folderMap.get(normalizeKey(folder.parentId))?.push(folder); + } + + // Find the target folder in the folderPaths to get its full details + const targetFolderWithPath = folderPaths.find((f) => f.id === targetFolder!.id); + if (!targetFolderWithPath) { + throw new NotFoundError({ message: `Target folder path not found` }); + } + + // Recursively collect all folders under the target folder (descendants only) + const collectDescendants = ( + id: string + ): (TSecretFolders & { path: string; depth: number; environment: string })[] => { + const children = folderMap.get(normalizeKey(id)) || []; + return [...children, ...children.flatMap((child) => collectDescendants(child.id))]; + }; + + const targetFolderDescendants = collectDescendants(targetFolder.id); + + // Include the target folder itself plus all its descendants + const foldersToCheck = [targetFolderWithPath, ...targetFolderDescendants]; + + const folderPolicyPaths = foldersToCheck.map((folder) => ({ + path: folder.path, + id: folder.id + })); + + // get secrets under the given folders + const secrets = await secretV2BridgeDAL.findByFolderIds({ + folderIds: folderPolicyPaths.map((p) => p.id) + }); + + for await (const folderPolicyPath of folderPolicyPaths) { + // eslint-disable-next-line no-continue + if (!secrets.some((s) => s.folderId === folderPolicyPath.id)) continue; + + const policy = await secretApprovalPolicyService.getSecretApprovalPolicy( + projectId, + env.slug, + folderPolicyPath.path + ); + + // if there is a policy and there are secrets under the given folder, throw error + if (policy) { + throw new BadRequestError({ + message: `You cannot delete the selected folder because it contains one or more secrets that are protected by the change policy "${policy.name}" at folder path "${folderPolicyPath.path}". Please remove the secrets at folder path "${folderPolicyPath.path}" and try again.`, + name: "DeleteFolderProtectedByPolicy" + }); + } + } + }; + const deleteFolder = async ({ projectId, actor, @@ -479,8 +582,7 @@ export const secretFolderServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -498,18 +600,42 @@ export const secretFolderServiceFactory = ({ message: `Folder with path '${secretPath}' in environment with slug '${environment}' not found` }); + await $checkFolderPolicy({ projectId, env, parentId: parentFolder.id, idOrName }); + + let folderToDelete = await folderDAL + .findOne({ + envId: env.id, + name: idOrName, + parentId: parentFolder.id, + isReserved: false + }) + .catch(() => null); + + if (!folderToDelete && uuidValidate(idOrName)) { + folderToDelete = await folderDAL + .findOne({ + envId: env.id, + id: idOrName, + parentId: parentFolder.id, + isReserved: false + }) + .catch(() => null); + } + + if (!folderToDelete) { + throw new NotFoundError({ message: `Folder with ID '${idOrName}' not found` }); + } + const [doc] = await folderDAL.delete( { envId: env.id, - [uuidValidate(idOrName) ? "id" : "name"]: idOrName, + id: folderToDelete.id, parentId: parentFolder.id, isReserved: false }, tx ); - if (!doc) throw new NotFoundError({ message: `Failed to delete folder with ID '${idOrName}', not found` }); - const folderVersions = await folderVersionDAL.findLatestFolderVersions([doc.id], tx); await folderCommitService.createCommit( @@ -562,8 +688,7 @@ export const secretFolderServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const env = await projectEnvDAL.findOne({ projectId, slug: environment }); @@ -631,8 +756,7 @@ export const secretFolderServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const envs = await projectEnvDAL.findBySlugs(projectId, environments); @@ -673,8 +797,7 @@ export const secretFolderServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const envs = await projectEnvDAL.findBySlugs(projectId, environments); @@ -709,8 +832,7 @@ export const secretFolderServiceFactory = ({ actorId, projectId: folder.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const [folderWithPath] = await folderDAL.findSecretPathByFolderIds(folder.projectId, [folder.id]); @@ -738,8 +860,7 @@ export const secretFolderServiceFactory = ({ actorId: actor.id, projectId, actorAuthMethod: actor.authMethod, - actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId: actor.orgId }); const envs = await projectEnvDAL.findBySlugs(projectId, environments); @@ -766,8 +887,7 @@ export const secretFolderServiceFactory = ({ actorId: actor.id, projectId, actorAuthMethod: actor.authMethod, - actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId: actor.orgId }); const environments = await projectEnvDAL.find({ projectId }); diff --git a/backend/src/services/secret-import/secret-import-service.ts b/backend/src/services/secret-import/secret-import-service.ts index 403484fc2..297c5d01f 100644 --- a/backend/src/services/secret-import/secret-import-service.ts +++ b/backend/src/services/secret-import/secret-import-service.ts @@ -2,7 +2,7 @@ import path from "node:path"; import { ForbiddenError, subject } from "@casl/ability"; -import { ActionProjectType, TableName } from "@app/db/schemas"; +import { TableName } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { hasSecretReadValueOrDescribePermission, @@ -87,8 +87,7 @@ export const secretImportServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); // check if user has permission to import into destination path @@ -205,8 +204,7 @@ export const secretImportServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -303,8 +301,7 @@ export const secretImportServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -378,8 +375,7 @@ export const secretImportServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); // check if user has permission to import into destination path @@ -455,8 +451,7 @@ export const secretImportServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, @@ -489,8 +484,7 @@ export const secretImportServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const filteredEnvironments = []; for (const environment of environments) { @@ -543,8 +537,7 @@ export const secretImportServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, @@ -593,8 +586,7 @@ export const secretImportServiceFactory = ({ actorId, projectId: folder.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -642,8 +634,7 @@ export const secretImportServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, @@ -678,8 +669,7 @@ export const secretImportServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, @@ -762,8 +752,7 @@ export const secretImportServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const filteredEnvironments = []; for (const environment of environments) { @@ -815,8 +804,7 @@ export const secretImportServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); if ( permission.cannot( diff --git a/backend/src/services/secret-sync/secret-sync-enums.ts b/backend/src/services/secret-sync/secret-sync-enums.ts index b70b37caf..62730c3da 100644 --- a/backend/src/services/secret-sync/secret-sync-enums.ts +++ b/backend/src/services/secret-sync/secret-sync-enums.ts @@ -20,7 +20,8 @@ export enum SecretSync { Render = "render", Flyio = "flyio", GitLab = "gitlab", - CloudflarePages = "cloudflare-pages" + CloudflarePages = "cloudflare-pages", + Zabbix = "zabbix" } export enum SecretSyncInitialSyncBehavior { diff --git a/backend/src/services/secret-sync/secret-sync-fns.ts b/backend/src/services/secret-sync/secret-sync-fns.ts index 9d0513a2c..d058d355e 100644 --- a/backend/src/services/secret-sync/secret-sync-fns.ts +++ b/backend/src/services/secret-sync/secret-sync-fns.ts @@ -45,6 +45,7 @@ import { TEAMCITY_SYNC_LIST_OPTION, TeamCitySyncFns } from "./teamcity"; import { TERRAFORM_CLOUD_SYNC_LIST_OPTION, TerraformCloudSyncFns } from "./terraform-cloud"; import { VERCEL_SYNC_LIST_OPTION, VercelSyncFns } from "./vercel"; import { WINDMILL_SYNC_LIST_OPTION, WindmillSyncFns } from "./windmill"; +import { ZABBIX_SYNC_LIST_OPTION, ZabbixSyncFns } from "./zabbix"; const SECRET_SYNC_LIST_OPTIONS: Record = { [SecretSync.AWSParameterStore]: AWS_PARAMETER_STORE_SYNC_LIST_OPTION, @@ -68,7 +69,8 @@ const SECRET_SYNC_LIST_OPTIONS: Record = { [SecretSync.Render]: RENDER_SYNC_LIST_OPTION, [SecretSync.Flyio]: FLYIO_SYNC_LIST_OPTION, [SecretSync.GitLab]: GITLAB_SYNC_LIST_OPTION, - [SecretSync.CloudflarePages]: CLOUDFLARE_PAGES_SYNC_LIST_OPTION + [SecretSync.CloudflarePages]: CLOUDFLARE_PAGES_SYNC_LIST_OPTION, + [SecretSync.Zabbix]: ZABBIX_SYNC_LIST_OPTION }; export const listSecretSyncOptions = () => { @@ -236,6 +238,8 @@ export const SecretSyncFns = { return GitLabSyncFns.syncSecrets(secretSync, schemaSecretMap, { appConnectionDAL, kmsService }); case SecretSync.CloudflarePages: return CloudflarePagesSyncFns.syncSecrets(secretSync, schemaSecretMap); + case SecretSync.Zabbix: + return ZabbixSyncFns.syncSecrets(secretSync, schemaSecretMap); default: throw new Error( `Unhandled sync destination for sync secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}` @@ -328,6 +332,9 @@ export const SecretSyncFns = { case SecretSync.CloudflarePages: secretMap = await CloudflarePagesSyncFns.getSecrets(secretSync); break; + case SecretSync.Zabbix: + secretMap = await ZabbixSyncFns.getSecrets(secretSync); + break; default: throw new Error( `Unhandled sync destination for get secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}` @@ -405,6 +412,8 @@ export const SecretSyncFns = { return GitLabSyncFns.removeSecrets(secretSync, schemaSecretMap, { appConnectionDAL, kmsService }); case SecretSync.CloudflarePages: return CloudflarePagesSyncFns.removeSecrets(secretSync, schemaSecretMap); + case SecretSync.Zabbix: + return ZabbixSyncFns.removeSecrets(secretSync, schemaSecretMap); default: throw new Error( `Unhandled sync destination for remove secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}` diff --git a/backend/src/services/secret-sync/secret-sync-maps.ts b/backend/src/services/secret-sync/secret-sync-maps.ts index 1dc0ea6c0..25df5d0b4 100644 --- a/backend/src/services/secret-sync/secret-sync-maps.ts +++ b/backend/src/services/secret-sync/secret-sync-maps.ts @@ -23,7 +23,8 @@ export const SECRET_SYNC_NAME_MAP: Record = { [SecretSync.Render]: "Render", [SecretSync.Flyio]: "Fly.io", [SecretSync.GitLab]: "GitLab", - [SecretSync.CloudflarePages]: "Cloudflare Pages" + [SecretSync.CloudflarePages]: "Cloudflare Pages", + [SecretSync.Zabbix]: "Zabbix" }; export const SECRET_SYNC_CONNECTION_MAP: Record = { @@ -48,7 +49,8 @@ export const SECRET_SYNC_CONNECTION_MAP: Record = { [SecretSync.Render]: AppConnection.Render, [SecretSync.Flyio]: AppConnection.Flyio, [SecretSync.GitLab]: AppConnection.GitLab, - [SecretSync.CloudflarePages]: AppConnection.Cloudflare + [SecretSync.CloudflarePages]: AppConnection.Cloudflare, + [SecretSync.Zabbix]: AppConnection.Zabbix }; export const SECRET_SYNC_PLAN_MAP: Record = { @@ -73,5 +75,6 @@ export const SECRET_SYNC_PLAN_MAP: Record = { [SecretSync.Render]: SecretSyncPlanType.Regular, [SecretSync.Flyio]: SecretSyncPlanType.Regular, [SecretSync.GitLab]: SecretSyncPlanType.Regular, - [SecretSync.CloudflarePages]: SecretSyncPlanType.Regular + [SecretSync.CloudflarePages]: SecretSyncPlanType.Regular, + [SecretSync.Zabbix]: SecretSyncPlanType.Regular }; diff --git a/backend/src/services/secret-sync/secret-sync-queue.ts b/backend/src/services/secret-sync/secret-sync-queue.ts index a8ff94e82..2acba91e5 100644 --- a/backend/src/services/secret-sync/secret-sync-queue.ts +++ b/backend/src/services/secret-sync/secret-sync-queue.ts @@ -868,7 +868,7 @@ export const secretSyncQueueFactory = ({ secretPath: folder?.path, environment: environment?.name, projectName: project.name, - syncUrl: `${appCfg.SITE_URL}/secret-manager/${projectId}/integrations/secret-syncs/${destination}/${secretSync.id}` + syncUrl: `${appCfg.SITE_URL}/projects/${projectId}/secret-manager/integrations/secret-syncs/${destination}/${secretSync.id}` } }); }; diff --git a/backend/src/services/secret-sync/secret-sync-service.ts b/backend/src/services/secret-sync/secret-sync-service.ts index 3fdb7fea6..bd52c0b77 100644 --- a/backend/src/services/secret-sync/secret-sync-service.ts +++ b/backend/src/services/secret-sync/secret-sync-service.ts @@ -1,6 +1,5 @@ import { ForbiddenError, subject } from "@casl/ability"; -import { ActionProjectType } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { throwIfMissingSecretReadValueOrDescribePermission } from "@app/ee/services/permission/permission-fns"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; @@ -75,7 +74,7 @@ export const secretSyncServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretManager, + projectId }); @@ -111,7 +110,7 @@ export const secretSyncServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretManager, + projectId }); @@ -154,7 +153,7 @@ export const secretSyncServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretManager, + projectId: secretSync.projectId }); @@ -196,7 +195,7 @@ export const secretSyncServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretManager, + projectId: secretSync.projectId }); @@ -234,7 +233,7 @@ export const secretSyncServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretManager, + projectId }); @@ -314,7 +313,7 @@ export const secretSyncServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretManager, + projectId: secretSync.projectId }); @@ -430,7 +429,7 @@ export const secretSyncServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretManager, + projectId: secretSync.projectId }); @@ -507,7 +506,7 @@ export const secretSyncServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretManager, + projectId: secretSync.projectId }); @@ -579,7 +578,7 @@ export const secretSyncServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretManager, + projectId: secretSync.projectId }); @@ -645,7 +644,7 @@ export const secretSyncServiceFactory = ({ actorId: actor.id, actorAuthMethod: actor.authMethod, actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretManager, + projectId: secretSync.projectId }); diff --git a/backend/src/services/secret-sync/secret-sync-types.ts b/backend/src/services/secret-sync/secret-sync-types.ts index a31183280..b076ea9c4 100644 --- a/backend/src/services/secret-sync/secret-sync-types.ts +++ b/backend/src/services/secret-sync/secret-sync-types.ts @@ -113,6 +113,7 @@ import { TTerraformCloudSyncWithCredentials } from "./terraform-cloud"; import { TVercelSync, TVercelSyncInput, TVercelSyncListItem, TVercelSyncWithCredentials } from "./vercel"; +import { TZabbixSync, TZabbixSyncInput, TZabbixSyncListItem, TZabbixSyncWithCredentials } from "./zabbix"; export type TSecretSync = | TAwsParameterStoreSync @@ -136,7 +137,8 @@ export type TSecretSync = | TRenderSync | TFlyioSync | TGitLabSync - | TCloudflarePagesSync; + | TCloudflarePagesSync + | TZabbixSync; export type TSecretSyncWithCredentials = | TAwsParameterStoreSyncWithCredentials @@ -160,7 +162,8 @@ export type TSecretSyncWithCredentials = | TRenderSyncWithCredentials | TFlyioSyncWithCredentials | TGitLabSyncWithCredentials - | TCloudflarePagesSyncWithCredentials; + | TCloudflarePagesSyncWithCredentials + | TZabbixSyncWithCredentials; export type TSecretSyncInput = | TAwsParameterStoreSyncInput @@ -184,7 +187,8 @@ export type TSecretSyncInput = | TRenderSyncInput | TFlyioSyncInput | TGitLabSyncInput - | TCloudflarePagesSyncInput; + | TCloudflarePagesSyncInput + | TZabbixSyncInput; export type TSecretSyncListItem = | TAwsParameterStoreSyncListItem @@ -208,7 +212,8 @@ export type TSecretSyncListItem = | TRenderSyncListItem | TFlyioSyncListItem | TGitLabSyncListItem - | TCloudflarePagesSyncListItem; + | TCloudflarePagesSyncListItem + | TZabbixSyncListItem; export type TSyncOptionsConfig = { canImportSecrets: boolean; diff --git a/backend/src/services/secret-sync/zabbix/index.ts b/backend/src/services/secret-sync/zabbix/index.ts new file mode 100644 index 000000000..a49d8e14c --- /dev/null +++ b/backend/src/services/secret-sync/zabbix/index.ts @@ -0,0 +1,5 @@ +export * from "./zabbix-sync-constants"; +export * from "./zabbix-sync-enums"; +export * from "./zabbix-sync-fns"; +export * from "./zabbix-sync-schemas"; +export * from "./zabbix-sync-types"; diff --git a/backend/src/services/secret-sync/zabbix/zabbix-sync-constants.ts b/backend/src/services/secret-sync/zabbix/zabbix-sync-constants.ts new file mode 100644 index 000000000..51c1ca793 --- /dev/null +++ b/backend/src/services/secret-sync/zabbix/zabbix-sync-constants.ts @@ -0,0 +1,10 @@ +import { AppConnection } from "@app/services/app-connection/app-connection-enums"; +import { SecretSync } from "@app/services/secret-sync/secret-sync-enums"; +import { TSecretSyncListItem } from "@app/services/secret-sync/secret-sync-types"; + +export const ZABBIX_SYNC_LIST_OPTION: TSecretSyncListItem = { + name: "Zabbix", + destination: SecretSync.Zabbix, + connection: AppConnection.Zabbix, + canImportSecrets: true +}; diff --git a/backend/src/services/secret-sync/zabbix/zabbix-sync-enums.ts b/backend/src/services/secret-sync/zabbix/zabbix-sync-enums.ts new file mode 100644 index 000000000..8f4c8c5d6 --- /dev/null +++ b/backend/src/services/secret-sync/zabbix/zabbix-sync-enums.ts @@ -0,0 +1,4 @@ +export enum ZabbixSyncScope { + Global = "global", + Host = "host" +} diff --git a/backend/src/services/secret-sync/zabbix/zabbix-sync-fns.ts b/backend/src/services/secret-sync/zabbix/zabbix-sync-fns.ts new file mode 100644 index 000000000..18f9061ec --- /dev/null +++ b/backend/src/services/secret-sync/zabbix/zabbix-sync-fns.ts @@ -0,0 +1,285 @@ +import RE2 from "re2"; + +import { request } from "@app/lib/config/request"; +import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator"; +import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors"; +import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns"; +import { TSecretMap } from "@app/services/secret-sync/secret-sync-types"; +import { + TZabbixSecret, + TZabbixSyncWithCredentials, + ZabbixApiResponse, + ZabbixMacroCreateResponse, + ZabbixMacroDeleteResponse +} from "@app/services/secret-sync/zabbix/zabbix-sync-types"; + +import { ZabbixSyncScope } from "./zabbix-sync-enums"; + +const TRAILING_SLASH_REGEX = new RE2("/+$"); +const MACRO_START_REGEX = new RE2("^\\{\\$"); +const MACRO_END_REGEX = new RE2("\\}$"); + +const extractMacroKey = (macro: string): string => { + return macro.replace(MACRO_START_REGEX, "").replace(MACRO_END_REGEX, ""); +}; + +// Helper function to handle Zabbix API responses and errors +const handleZabbixResponse = (response: ZabbixApiResponse): T => { + if (response.data.error) { + const errorMessage = response.data.error.data + ? `${response.data.error.message}: ${response.data.error.data}` + : response.data.error.message; + throw new SecretSyncError({ + error: new Error(`Zabbix API Error (${response.data.error.code}): ${errorMessage}`) + }); + } + + if (response.data.result === undefined) { + throw new SecretSyncError({ + error: new Error("Zabbix API returned no result") + }); + } + + return response.data.result; +}; + +const listZabbixSecrets = async (apiToken: string, instanceUrl: string, hostId?: string): Promise => { + const apiUrl = `${instanceUrl.replace(TRAILING_SLASH_REGEX, "")}/api_jsonrpc.php`; + + // - jsonrpc: Specifies the JSON-RPC protocol version. + // - method: The API method to call, in this case "usermacro.get" for retrieving user macros. + // - id: A unique identifier for the request. Required by JSON-RPC but not used by the API for logic. Typically set to any integer. + const payload = { + jsonrpc: "2.0" as const, + method: "usermacro.get", + params: hostId ? { output: "extend", hostids: hostId } : { output: "extend", globalmacro: true }, + id: 1 + }; + + try { + const response: ZabbixApiResponse = await request.post(apiUrl, payload, { + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${apiToken}` + } + }); + + return handleZabbixResponse(response) || []; + } catch (error) { + throw new SecretSyncError({ + error: error instanceof Error ? error : new Error("Failed to list Zabbix secrets") + }); + } +}; + +const putZabbixSecrets = async ( + apiToken: string, + instanceUrl: string, + secretMap: TSecretMap, + destinationConfig: TZabbixSyncWithCredentials["destinationConfig"], + existingSecrets: TZabbixSecret[] +): Promise => { + const apiUrl = `${instanceUrl.replace(TRAILING_SLASH_REGEX, "")}/api_jsonrpc.php`; + const hostId = destinationConfig.scope === ZabbixSyncScope.Host ? destinationConfig.hostId : undefined; + + const existingMacroMap = new Map(existingSecrets.map((secret) => [secret.macro, secret])); + + for (const [key, secret] of Object.entries(secretMap)) { + const macroKey = `{$${key.toUpperCase()}}`; + const existingMacro = existingMacroMap.get(macroKey); + + try { + if (existingMacro) { + // Update existing macro + const updatePayload = { + jsonrpc: "2.0" as const, + method: hostId ? "usermacro.update" : "usermacro.updateglobal", + params: { + [hostId ? "hostmacroid" : "globalmacroid"]: existingMacro[hostId ? "hostmacroid" : "globalmacroid"], + value: secret.value, + type: destinationConfig.macroType, + description: secret.comment + }, + id: 1 + }; + + // eslint-disable-next-line no-await-in-loop + const response: ZabbixApiResponse = await request.post(apiUrl, updatePayload, { + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${apiToken}` + } + }); + + handleZabbixResponse(response); + } else { + // Create new macro + const createPayload = { + jsonrpc: "2.0" as const, + method: hostId ? "usermacro.create" : "usermacro.createglobal", + params: hostId + ? { + hostid: hostId, + macro: macroKey, + value: secret.value, + type: destinationConfig.macroType, + description: secret.comment + } + : { + macro: macroKey, + value: secret.value, + type: destinationConfig.macroType, + description: secret.comment + }, + id: 1 + }; + + // eslint-disable-next-line no-await-in-loop + const response: ZabbixApiResponse = await request.post(apiUrl, createPayload, { + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${apiToken}` + } + }); + + handleZabbixResponse(response); + } + } catch (error) { + throw new SecretSyncError({ + error: error instanceof Error ? error : new Error(`Failed to sync secret ${key}`) + }); + } + } +}; + +const deleteZabbixSecrets = async ( + apiToken: string, + instanceUrl: string, + keys: string[], + hostId?: string +): Promise => { + if (keys.length === 0) return; + + const apiUrl = `${instanceUrl.replace(TRAILING_SLASH_REGEX, "")}/api_jsonrpc.php`; + + try { + // Get existing macros to find their IDs + const existingSecrets = await listZabbixSecrets(apiToken, instanceUrl, hostId); + const macroIds = existingSecrets + .filter((secret) => keys.includes(secret.macro)) + .map((secret) => secret[hostId ? "hostmacroid" : "globalmacroid"]) + .filter(Boolean); + + if (macroIds.length === 0) return; + + const payload = { + jsonrpc: "2.0" as const, + method: hostId ? "usermacro.delete" : "usermacro.deleteglobal", + params: macroIds, + id: 1 + }; + + const response: ZabbixApiResponse = await request.post(apiUrl, payload, { + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${apiToken}` + } + }); + + handleZabbixResponse(response); + } catch (error) { + throw new SecretSyncError({ + error: error instanceof Error ? error : new Error("Failed to delete Zabbix secrets") + }); + } +}; + +export const ZabbixSyncFns = { + syncSecrets: async (secretSync: TZabbixSyncWithCredentials, secretMap: TSecretMap) => { + const { connection, environment, destinationConfig } = secretSync; + const { apiToken, instanceUrl } = connection.credentials; + await blockLocalAndPrivateIpAddresses(instanceUrl); + + const hostId = destinationConfig.scope === ZabbixSyncScope.Host ? destinationConfig.hostId : undefined; + let secrets: TZabbixSecret[] = []; + try { + secrets = await listZabbixSecrets(apiToken, instanceUrl, hostId); + } catch (error) { + throw new SecretSyncError({ + error: error instanceof Error ? error : new Error("Failed to list Zabbix secrets") + }); + } + + try { + await putZabbixSecrets(apiToken, instanceUrl, secretMap, destinationConfig, secrets); + } catch (error) { + throw new SecretSyncError({ + error: error instanceof Error ? error : new Error("Failed to sync secrets") + }); + } + + if (secretSync.syncOptions.disableSecretDeletion) return; + + try { + const shapedSecretMapKeys = Object.keys(secretMap).map((key) => key.toUpperCase()); + + const keys = secrets + .filter( + (secret) => + matchesSchema(secret.macro, environment?.slug || "", secretSync.syncOptions.keySchema) && + !shapedSecretMapKeys.includes(extractMacroKey(secret.macro)) + ) + .map((secret) => secret.macro); + + await deleteZabbixSecrets(apiToken, instanceUrl, keys, hostId); + } catch (error) { + throw new SecretSyncError({ + error: error instanceof Error ? error : new Error("Failed to delete orphaned secrets") + }); + } + }, + + removeSecrets: async (secretSync: TZabbixSyncWithCredentials, secretMap: TSecretMap) => { + const { connection, destinationConfig } = secretSync; + const { apiToken, instanceUrl } = connection.credentials; + await blockLocalAndPrivateIpAddresses(instanceUrl); + + const hostId = destinationConfig.scope === ZabbixSyncScope.Host ? destinationConfig.hostId : undefined; + + try { + const secrets = await listZabbixSecrets(apiToken, instanceUrl, hostId); + + const shapedSecretMapKeys = Object.keys(secretMap).map((key) => key.toUpperCase()); + const keys = secrets + .filter((secret) => shapedSecretMapKeys.includes(extractMacroKey(secret.macro))) + .map((secret) => secret.macro); + + await deleteZabbixSecrets(apiToken, instanceUrl, keys, hostId); + } catch (error) { + throw new SecretSyncError({ + error: error instanceof Error ? error : new Error("Failed to remove secrets") + }); + } + }, + + getSecrets: async (secretSync: TZabbixSyncWithCredentials) => { + const { connection, destinationConfig } = secretSync; + const { apiToken, instanceUrl } = connection.credentials; + await blockLocalAndPrivateIpAddresses(instanceUrl); + const hostId = destinationConfig.scope === ZabbixSyncScope.Host ? destinationConfig.hostId : undefined; + + try { + const secrets = await listZabbixSecrets(apiToken, instanceUrl, hostId); + return Object.fromEntries( + secrets.map((secret) => [ + extractMacroKey(secret.macro), + { value: secret.value ?? "", comment: secret.description } + ]) + ); + } catch (error) { + throw new SecretSyncError({ + error: error instanceof Error ? error : new Error("Failed to get secrets") + }); + } + } +}; diff --git a/backend/src/services/secret-sync/zabbix/zabbix-sync-schemas.ts b/backend/src/services/secret-sync/zabbix/zabbix-sync-schemas.ts new file mode 100644 index 000000000..94a729cb6 --- /dev/null +++ b/backend/src/services/secret-sync/zabbix/zabbix-sync-schemas.ts @@ -0,0 +1,67 @@ +import { z } from "zod"; + +import { SecretSyncs } from "@app/lib/api-docs"; +import { AppConnection } from "@app/services/app-connection/app-connection-enums"; +import { SecretSync } from "@app/services/secret-sync/secret-sync-enums"; +import { + BaseSecretSyncSchema, + GenericCreateSecretSyncFieldsSchema, + GenericUpdateSecretSyncFieldsSchema +} from "@app/services/secret-sync/secret-sync-schemas"; +import { TSyncOptionsConfig } from "@app/services/secret-sync/secret-sync-types"; + +import { ZabbixSyncScope } from "./zabbix-sync-enums"; + +const ZabbixSyncDestinationConfigSchema = z.discriminatedUnion("scope", [ + z.object({ + scope: z.literal(ZabbixSyncScope.Host).describe(SecretSyncs.DESTINATION_CONFIG.ZABBIX.scope), + hostId: z.string().trim().min(1, "Host required").max(255).describe(SecretSyncs.DESTINATION_CONFIG.ZABBIX.hostId), + hostName: z + .string() + .trim() + .min(1, "Host name required") + .max(255) + .describe(SecretSyncs.DESTINATION_CONFIG.ZABBIX.hostName), + macroType: z + .number() + .min(0, "Macro type required") + .max(1, "Macro type required") + .describe(SecretSyncs.DESTINATION_CONFIG.ZABBIX.macroType) + }), + z.object({ + scope: z.literal(ZabbixSyncScope.Global).describe(SecretSyncs.DESTINATION_CONFIG.ZABBIX.scope), + macroType: z + .number() + .min(0, "Macro type required") + .max(1, "Macro type required") + .describe(SecretSyncs.DESTINATION_CONFIG.ZABBIX.macroType) + }) +]); + +const ZabbixSyncOptionsConfig: TSyncOptionsConfig = { canImportSecrets: true }; + +export const ZabbixSyncSchema = BaseSecretSyncSchema(SecretSync.Zabbix, ZabbixSyncOptionsConfig).extend({ + destination: z.literal(SecretSync.Zabbix), + destinationConfig: ZabbixSyncDestinationConfigSchema +}); + +export const CreateZabbixSyncSchema = GenericCreateSecretSyncFieldsSchema( + SecretSync.Zabbix, + ZabbixSyncOptionsConfig +).extend({ + destinationConfig: ZabbixSyncDestinationConfigSchema +}); + +export const UpdateZabbixSyncSchema = GenericUpdateSecretSyncFieldsSchema( + SecretSync.Zabbix, + ZabbixSyncOptionsConfig +).extend({ + destinationConfig: ZabbixSyncDestinationConfigSchema.optional() +}); + +export const ZabbixSyncListItemSchema = z.object({ + name: z.literal("Zabbix"), + connection: z.literal(AppConnection.Zabbix), + destination: z.literal(SecretSync.Zabbix), + canImportSecrets: z.literal(true) +}); diff --git a/backend/src/services/secret-sync/zabbix/zabbix-sync-types.ts b/backend/src/services/secret-sync/zabbix/zabbix-sync-types.ts new file mode 100644 index 000000000..9640394d9 --- /dev/null +++ b/backend/src/services/secret-sync/zabbix/zabbix-sync-types.ts @@ -0,0 +1,75 @@ +import { z } from "zod"; + +import { TZabbixConnection } from "@app/services/app-connection/zabbix"; + +import { CreateZabbixSyncSchema, ZabbixSyncListItemSchema, ZabbixSyncSchema } from "./zabbix-sync-schemas"; + +export type TZabbixSync = z.infer; +export type TZabbixSyncInput = z.infer; +export type TZabbixSyncListItem = z.infer; + +export type TZabbixSyncWithCredentials = TZabbixSync & { + connection: TZabbixConnection; +}; + +export type TZabbixSecret = { + macro: string; + value: string; + description?: string; + globalmacroid?: string; + hostmacroid?: string; + hostid?: string; + type: number; + automatic?: string; +}; + +export interface ZabbixApiResponse { + data: { + jsonrpc: "2.0"; + result?: T; + error?: { + code: number; + message: string; + data?: string; + }; + id: number; + }; +} + +export interface ZabbixMacroCreateResponse { + hostmacroids?: string[]; + globalmacroids?: string[]; +} + +export interface ZabbixMacroUpdateResponse { + hostmacroids?: string[]; + globalmacroids?: string[]; +} + +export interface ZabbixMacroDeleteResponse { + hostmacroids?: string[]; + globalmacroids?: string[]; +} + +export enum ZabbixMacroType { + TEXT = 0, + SECRET = 1 +} + +export interface ZabbixMacroInput { + hostid?: string; + macro: string; + value: string; + description?: string; + type?: ZabbixMacroType; + automatic?: "0" | "1"; +} + +export interface ZabbixMacroUpdate { + hostmacroid?: string; + globalmacroid?: string; + value?: string; + description?: string; + type?: ZabbixMacroType; + automatic?: "0" | "1"; +} diff --git a/backend/src/services/secret-tag/secret-tag-service.ts b/backend/src/services/secret-tag/secret-tag-service.ts index 8a08c44dd..a4be06b4f 100644 --- a/backend/src/services/secret-tag/secret-tag-service.ts +++ b/backend/src/services/secret-tag/secret-tag-service.ts @@ -1,6 +1,5 @@ import { ForbiddenError } from "@casl/ability"; -import { ActionProjectType } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { BadRequestError, NotFoundError } from "@app/lib/errors"; @@ -29,8 +28,7 @@ export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSe actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Tags); @@ -61,8 +59,7 @@ export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSe actorId, projectId: tag.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Tags); @@ -79,8 +76,7 @@ export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSe actorId, projectId: tag.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Tags); @@ -97,8 +93,7 @@ export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSe actorId, projectId: tag.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Tags); @@ -114,8 +109,7 @@ export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSe actorId, projectId: tag.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Tags); @@ -128,8 +122,7 @@ export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSe actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Tags); diff --git a/backend/src/services/secret-v2-bridge/secret-v2-bridge-service.ts b/backend/src/services/secret-v2-bridge/secret-v2-bridge-service.ts index fc758b4f5..71180772f 100644 --- a/backend/src/services/secret-v2-bridge/secret-v2-bridge-service.ts +++ b/backend/src/services/secret-v2-bridge/secret-v2-bridge-service.ts @@ -2,14 +2,7 @@ import { ForbiddenError, MongoAbility, subject } from "@casl/ability"; import { Knex } from "knex"; import { z } from "zod"; -import { - ActionProjectType, - ProjectMembershipRole, - SecretsV2Schema, - SecretType, - TableName, - TSecretsV2 -} from "@app/db/schemas"; +import { ProjectMembershipRole, SecretsV2Schema, SecretType, TableName, TSecretsV2 } from "@app/db/schemas"; import { hasSecretReadValueOrDescribePermission, throwIfMissingSecretReadValueOrDescribePermission @@ -243,8 +236,7 @@ export const secretV2BridgeServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath); @@ -387,8 +379,7 @@ export const secretV2BridgeServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); if (inputSecret.newSecretName === "") { @@ -615,8 +606,7 @@ export const secretV2BridgeServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath); @@ -752,8 +742,7 @@ export const secretV2BridgeServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); throwIfMissingSecretReadValueOrDescribePermission(permission, ProjectPermissionSecretActions.DescribeSecret); } @@ -798,8 +787,7 @@ export const secretV2BridgeServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); throwIfMissingSecretReadValueOrDescribePermission(permission, ProjectPermissionSecretActions.DescribeSecret); @@ -898,8 +886,7 @@ export const secretV2BridgeServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); if (!isInternal) { throwIfMissingSecretReadValueOrDescribePermission(permission, ProjectPermissionSecretActions.DescribeSecret); @@ -952,8 +939,7 @@ export const secretV2BridgeServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); throwIfMissingSecretReadValueOrDescribePermission(permission, ProjectPermissionSecretActions.DescribeSecret); @@ -1221,8 +1207,7 @@ export const secretV2BridgeServiceFactory = ({ actorId, projectId: secret.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); throwIfMissingSecretReadValueOrDescribePermission(permission, ProjectPermissionSecretActions.ReadValue, { @@ -1285,8 +1270,7 @@ export const secretV2BridgeServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const folder = await folderDAL.findBySecretPath(projectId, environment, path); @@ -1497,8 +1481,7 @@ export const secretV2BridgeServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath); @@ -1665,8 +1648,7 @@ export const secretV2BridgeServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const secretsToUpdateGroupByPath = groupBy(inputSecrets, (el) => el.secretPath || defaultSecretPath); @@ -2016,8 +1998,7 @@ export const secretV2BridgeServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath); @@ -2173,8 +2154,7 @@ export const secretV2BridgeServiceFactory = ({ actorId, projectId: folder.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const canRead = @@ -2239,8 +2219,7 @@ export const secretV2BridgeServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); if (!hasRole(ProjectMembershipRole.Admin)) @@ -2287,8 +2266,7 @@ export const secretV2BridgeServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const sourceFolder = await folderDAL.findBySecretPath(projectId, sourceEnvironment, sourceSecretPath); @@ -2674,8 +2652,7 @@ export const secretV2BridgeServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); throwIfMissingSecretReadValueOrDescribePermission(permission, ProjectPermissionSecretActions.DescribeSecret, { @@ -2767,8 +2744,7 @@ export const secretV2BridgeServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); throwIfMissingSecretReadValueOrDescribePermission(permission, ProjectPermissionSecretActions.DescribeSecret, { environment, @@ -2892,8 +2868,7 @@ export const secretV2BridgeServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const canRead = diff --git a/backend/src/services/secret/secret-fns.ts b/backend/src/services/secret/secret-fns.ts index 6f6358779..937367d67 100644 --- a/backend/src/services/secret/secret-fns.ts +++ b/backend/src/services/secret/secret-fns.ts @@ -3,7 +3,6 @@ import path from "path"; import RE2 from "re2"; import { - ActionProjectType, SecretEncryptionAlgo, SecretKeyEncoding, SecretType, @@ -182,8 +181,7 @@ export const recursivelyGetSecretPaths = ({ actorId: auth.actorId, projectId, actorAuthMethod: auth.actorAuthMethod, - actorOrgId: auth.actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId: auth.actorOrgId }); // Filter out paths that the user does not have permission to access, and paths that are not in the current path diff --git a/backend/src/services/secret/secret-queue.ts b/backend/src/services/secret/secret-queue.ts index 731eb6951..dbca65385 100644 --- a/backend/src/services/secret/secret-queue.ts +++ b/backend/src/services/secret/secret-queue.ts @@ -746,7 +746,7 @@ export const secretQueueFactory = ({ environment: jobPayload.environmentName, count: jobPayload.count, projectName: project.name, - integrationUrl: `${appCfg.SITE_URL}/secret-manager/${project.id}/integrations?selectedTab=native-integrations` + integrationUrl: `${appCfg.SITE_URL}/projects/${project.id}/secret-manager/integrations?selectedTab=native-integrations` } }); } diff --git a/backend/src/services/secret/secret-service.ts b/backend/src/services/secret/secret-service.ts index 5fa58ce90..83e586360 100644 --- a/backend/src/services/secret/secret-service.ts +++ b/backend/src/services/secret/secret-service.ts @@ -3,7 +3,6 @@ import { ForbiddenError, subject } from "@casl/ability"; import { - ActionProjectType, ProjectMembershipRole, ProjectUpgradeStatus, ProjectVersion, @@ -210,8 +209,7 @@ export const secretServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -328,8 +326,7 @@ export const secretServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -489,8 +486,7 @@ export const secretServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -605,8 +601,7 @@ export const secretServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); let paths: { folderId: string; path: string }[] = []; @@ -711,8 +706,7 @@ export const secretServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); throwIfMissingSecretReadValueOrDescribePermission(permission, ProjectPermissionSecretActions.ReadValue, { environment, @@ -817,8 +811,7 @@ export const secretServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionSecretActions.Create, @@ -904,8 +897,7 @@ export const secretServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -1027,8 +1019,7 @@ export const secretServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionSecretActions.Delete, @@ -2527,8 +2518,7 @@ export const secretServiceFactory = ({ actorId, projectId: folder.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback); const secretVersions = await secretVersionDAL.findBySecretId(secretId, { @@ -2620,8 +2610,7 @@ export const secretServiceFactory = ({ actorId, projectId: project.id, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -2726,8 +2715,7 @@ export const secretServiceFactory = ({ actorId, projectId: project.id, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan( @@ -2833,8 +2821,7 @@ export const secretServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); if (!hasRole(ProjectMembershipRole.Admin)) @@ -2919,8 +2906,7 @@ export const secretServiceFactory = ({ actorId, projectId: project.id, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); const { botKey } = await projectBotService.getBotKey(project.id); @@ -3327,8 +3313,7 @@ export const secretServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); if (!hasRole(ProjectMembershipRole.Admin)) @@ -3356,8 +3341,7 @@ export const secretServiceFactory = ({ actorId: actor.id, projectId: params.projectId, actorAuthMethod: actor.authMethod, - actorOrgId: actor.orgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId: actor.orgId }); const secrets = secretV2BridgeService.getSecretsByFolderMappings({ ...params, userId: actor.id }, permission); diff --git a/backend/src/services/service-token/service-token-service.ts b/backend/src/services/service-token/service-token-service.ts index 030276d16..07362ff65 100644 --- a/backend/src/services/service-token/service-token-service.ts +++ b/backend/src/services/service-token/service-token-service.ts @@ -1,6 +1,5 @@ import { ForbiddenError, subject } from "@casl/ability"; -import { ActionProjectType } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionActions, @@ -66,8 +65,7 @@ export const serviceTokenServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.ServiceTokens); @@ -122,8 +120,7 @@ export const serviceTokenServiceFactory = ({ actorId, projectId: serviceToken.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.SecretManager + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.ServiceTokens); @@ -157,8 +154,7 @@ export const serviceTokenServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.ServiceTokens); @@ -214,7 +210,7 @@ export const serviceTokenServiceFactory = ({ substitutions: { tokenName: token.name, projectName: token.projectName, - url: `${appCfg.SITE_URL}/secret-manager/${token.projectId}/access-management?selectedTab=service-tokens` + url: `${appCfg.SITE_URL}/projects/${token.projectId}/secret-manager/access-management?selectedTab=service-tokens` } }); await serviceTokenDAL.update({ id: token.id }, { expiryNotificationSent: true }); diff --git a/backend/src/services/slack/slack-fns.ts b/backend/src/services/slack/slack-fns.ts index d4f5e29cc..bee414179 100644 --- a/backend/src/services/slack/slack-fns.ts +++ b/backend/src/services/slack/slack-fns.ts @@ -50,7 +50,7 @@ const buildSlackPayload = (notification: TNotification) => { *Secret path*: ${payload.secretPath || "/"} *Secret Key${payload.secretKeys.length > 1 ? "s" : ""}*: ${payload.secretKeys.join(", ")} -View the complete details <${appCfg.SITE_URL}/secret-manager/${payload.projectId}/approval?requestId=${ +View the complete details <${appCfg.SITE_URL}/projects/${payload.projectId}/secret-manager/approval?requestId=${ payload.requestId }|here>.`; diff --git a/backend/src/services/super-admin/super-admin-service.ts b/backend/src/services/super-admin/super-admin-service.ts index 5c1b8f6f6..59db26147 100644 --- a/backend/src/services/super-admin/super-admin-service.ts +++ b/backend/src/services/super-admin/super-admin-service.ts @@ -4,7 +4,13 @@ import jwt from "jsonwebtoken"; import { IdentityAuthMethod, OrgMembershipRole, TSuperAdmin, TSuperAdminUpdate } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { PgSqlLock, TKeyStoreFactory } from "@app/keystore/keystore"; -import { getConfig } from "@app/lib/config/env"; +import { + getConfig, + getOriginalConfig, + overrideEnvConfig, + overwriteSchema, + validateOverrides +} from "@app/lib/config/env"; import { crypto } from "@app/lib/crypto/cryptography"; import { generateUserSrpKeys, getUserPrivateKey } from "@app/lib/crypto/srp"; import { BadRequestError, NotFoundError } from "@app/lib/errors"; @@ -32,6 +38,7 @@ import { TInvalidateCacheQueueFactory } from "./invalidate-cache-queue"; import { TSuperAdminDALFactory } from "./super-admin-dal"; import { CacheType, + EnvOverrides, LoginMethod, TAdminBootstrapInstanceDTO, TAdminGetIdentitiesDTO, @@ -234,6 +241,45 @@ export const superAdminServiceFactory = ({ adminIntegrationsConfig = config; }; + const getEnvOverrides = async () => { + const serverCfg = await serverCfgDAL.findById(ADMIN_CONFIG_DB_UUID); + + if (!serverCfg || !serverCfg.encryptedEnvOverrides) { + return {}; + } + + const decrypt = kmsService.decryptWithRootKey(); + + const overrides = JSON.parse(decrypt(serverCfg.encryptedEnvOverrides).toString()) as Record; + + return overrides; + }; + + const getEnvOverridesOrganized = async (): Promise => { + const overrides = await getEnvOverrides(); + const ogConfig = getOriginalConfig(); + + return Object.fromEntries( + Object.entries(overwriteSchema).map(([groupKey, groupDef]) => [ + groupKey, + { + name: groupDef.name, + fields: groupDef.fields.map(({ key, description }) => ({ + key, + description, + value: overrides[key] || "", + hasEnvEntry: !!(ogConfig as unknown as Record)[key] + })) + } + ]) + ); + }; + + const $syncEnvConfig = async () => { + const config = await getEnvOverrides(); + overrideEnvConfig(config); + }; + const updateServerCfg = async ( data: TSuperAdminUpdate & { slackClientId?: string; @@ -246,6 +292,7 @@ export const superAdminServiceFactory = ({ gitHubAppConnectionSlug?: string; gitHubAppConnectionId?: string; gitHubAppConnectionPrivateKey?: string; + envOverrides?: Record; }, userId: string ) => { @@ -374,6 +421,17 @@ export const superAdminServiceFactory = ({ gitHubAppConnectionSettingsUpdated = true; } + let envOverridesUpdated = false; + if (data.envOverrides !== undefined) { + // Verify input format + validateOverrides(data.envOverrides); + + const encryptedEnvOverrides = encryptWithRoot(Buffer.from(JSON.stringify(data.envOverrides))); + updatedData.encryptedEnvOverrides = encryptedEnvOverrides; + updatedData.envOverrides = undefined; + envOverridesUpdated = true; + } + const updatedServerCfg = await serverCfgDAL.updateById(ADMIN_CONFIG_DB_UUID, updatedData); await keyStore.setItemWithExpiry(ADMIN_CONFIG_KEY, ADMIN_CONFIG_KEY_EXP, JSON.stringify(updatedServerCfg)); @@ -382,6 +440,10 @@ export const superAdminServiceFactory = ({ await $syncAdminIntegrationConfig(); } + if (envOverridesUpdated) { + await $syncEnvConfig(); + } + if ( updatedServerCfg.encryptedMicrosoftTeamsAppId && updatedServerCfg.encryptedMicrosoftTeamsClientSecret && @@ -816,6 +878,18 @@ export const superAdminServiceFactory = ({ return job; }; + const initializeEnvConfigSync = async () => { + logger.info("Setting up background sync process for environment overrides"); + + await $syncEnvConfig(); + + // sync every 5 minutes + const job = new CronJob("*/5 * * * *", $syncEnvConfig); + job.start(); + + return job; + }; + return { initServerCfg, updateServerCfg, @@ -835,6 +909,9 @@ export const superAdminServiceFactory = ({ getOrganizations, deleteOrganization, deleteOrganizationMembership, - initializeAdminIntegrationConfigSync + initializeAdminIntegrationConfigSync, + initializeEnvConfigSync, + getEnvOverrides, + getEnvOverridesOrganized }; }; diff --git a/backend/src/services/super-admin/super-admin-types.ts b/backend/src/services/super-admin/super-admin-types.ts index 205c59f2c..b57a015a4 100644 --- a/backend/src/services/super-admin/super-admin-types.ts +++ b/backend/src/services/super-admin/super-admin-types.ts @@ -1,3 +1,5 @@ +import { TEnvConfig } from "@app/lib/config/env"; + export type TAdminSignUpDTO = { email: string; password: string; @@ -74,3 +76,10 @@ export type TAdminIntegrationConfig = { privateKey: string; }; }; + +export interface EnvOverrides { + [key: string]: { + name: string; + fields: { key: keyof TEnvConfig; value: string; hasEnvEntry: boolean; description?: string }[]; + }; +} diff --git a/backend/src/services/webhook/webhook-service.ts b/backend/src/services/webhook/webhook-service.ts index eb58ee5bd..ba3f2170a 100644 --- a/backend/src/services/webhook/webhook-service.ts +++ b/backend/src/services/webhook/webhook-service.ts @@ -1,6 +1,6 @@ import { ForbiddenError } from "@casl/ability"; -import { ActionProjectType, TWebhooksInsert } from "@app/db/schemas"; +import { TWebhooksInsert } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { NotFoundError } from "@app/lib/errors"; @@ -54,8 +54,7 @@ export const webhookServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Webhooks); const env = await projectEnvDAL.findOne({ projectId, slug: environment }); @@ -93,8 +92,7 @@ export const webhookServiceFactory = ({ actorId, projectId: webhook.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Webhooks); @@ -111,8 +109,7 @@ export const webhookServiceFactory = ({ actorId, projectId: webhook.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Webhooks); @@ -129,8 +126,7 @@ export const webhookServiceFactory = ({ actorId, projectId: webhook.projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); const project = await projectDAL.findById(webhook.projectId); @@ -181,8 +177,7 @@ export const webhookServiceFactory = ({ actorId, projectId, actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any + actorOrgId }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Webhooks); diff --git a/cli/detect/cmd/scm/scm.go b/cli/detect/cmd/scm/scm.go index 66868aadc..dddeffdf5 100644 --- a/cli/detect/cmd/scm/scm.go +++ b/cli/detect/cmd/scm/scm.go @@ -35,6 +35,7 @@ const ( GitHubPlatform GitLabPlatform AzureDevOpsPlatform + BitBucketPlatform // TODO: Add others. ) @@ -45,6 +46,7 @@ func (p Platform) String() string { "github", "gitlab", "azuredevops", + "bitbucket", }[p] } @@ -60,6 +62,8 @@ func PlatformFromString(s string) (Platform, error) { return GitLabPlatform, nil case "azuredevops": return AzureDevOpsPlatform, nil + case "bitbucket": + return BitBucketPlatform, nil default: return UnknownPlatform, fmt.Errorf("invalid scm platform value: %s", s) } diff --git a/cli/detect/git.go b/cli/detect/git.go index ddde0757d..83ed8a853 100644 --- a/cli/detect/git.go +++ b/cli/detect/git.go @@ -208,6 +208,8 @@ func platformFromHost(u *url.URL) scm.Platform { return scm.GitLabPlatform case "dev.azure.com", "visualstudio.com": return scm.AzureDevOpsPlatform + case "bitbucket.org": + return scm.BitBucketPlatform default: return scm.UnknownPlatform } diff --git a/cli/detect/utils.go b/cli/detect/utils.go index 255d01fbe..84b1017fc 100644 --- a/cli/detect/utils.go +++ b/cli/detect/utils.go @@ -112,6 +112,15 @@ func createScmLink(scmPlatform scm.Platform, remoteUrl string, finding report.Fi // This is a bit dirty, but Azure DevOps does not highlight the line when the lineStartColumn and lineEndColumn are not provided link += "&lineStartColumn=1&lineEndColumn=10000000&type=2&lineStyle=plain&_a=files" return link + case scm.BitBucketPlatform: + link := fmt.Sprintf("%s/src/%s/%s", remoteUrl, finding.Commit, filePath) + if finding.StartLine != 0 { + link += fmt.Sprintf("#lines-%d", finding.StartLine) + } + if finding.EndLine != finding.StartLine { + link += fmt.Sprintf(":%d", finding.EndLine) + } + return link default: // This should never happen. return "" diff --git a/cli/packages/cmd/scan.go b/cli/packages/cmd/scan.go index 42ff0f1e1..4a721d2c5 100644 --- a/cli/packages/cmd/scan.go +++ b/cli/packages/cmd/scan.go @@ -337,9 +337,7 @@ var scanCmd = &cobra.Command{ if gitCmd, err = sources.NewGitLogCmd(source, logOpts); err != nil { logging.Fatal().Err(err).Msg("could not create Git cmd") } - if scmPlatform, err = scm.PlatformFromString("github"); err != nil { - logging.Fatal().Err(err).Send() - } + scmPlatform = scm.UnknownPlatform remote = detect.NewRemoteInfo(scmPlatform, source) if findings, err = detector.DetectGit(gitCmd, remote); err != nil { diff --git a/docs/api-reference/endpoints/app-connections/zabbix/available.mdx b/docs/api-reference/endpoints/app-connections/zabbix/available.mdx new file mode 100644 index 000000000..49488471e --- /dev/null +++ b/docs/api-reference/endpoints/app-connections/zabbix/available.mdx @@ -0,0 +1,4 @@ +--- +title: "Available" +openapi: "GET /api/v1/app-connections/zabbix/available" +--- diff --git a/docs/api-reference/endpoints/app-connections/zabbix/create.mdx b/docs/api-reference/endpoints/app-connections/zabbix/create.mdx new file mode 100644 index 000000000..a11b01309 --- /dev/null +++ b/docs/api-reference/endpoints/app-connections/zabbix/create.mdx @@ -0,0 +1,8 @@ +--- +title: "Create" +openapi: "POST /api/v1/app-connections/zabbix" +--- + + + Check out the configuration docs for [Zabbix Connections](/integrations/app-connections/zabbix) to learn how to obtain the required credentials. + diff --git a/docs/api-reference/endpoints/app-connections/zabbix/delete.mdx b/docs/api-reference/endpoints/app-connections/zabbix/delete.mdx new file mode 100644 index 000000000..95b34e814 --- /dev/null +++ b/docs/api-reference/endpoints/app-connections/zabbix/delete.mdx @@ -0,0 +1,4 @@ +--- +title: "Delete" +openapi: "DELETE /api/v1/app-connections/zabbix/{connectionId}" +--- diff --git a/docs/api-reference/endpoints/app-connections/zabbix/get-by-id.mdx b/docs/api-reference/endpoints/app-connections/zabbix/get-by-id.mdx new file mode 100644 index 000000000..46306b035 --- /dev/null +++ b/docs/api-reference/endpoints/app-connections/zabbix/get-by-id.mdx @@ -0,0 +1,4 @@ +--- +title: "Get by ID" +openapi: "GET /api/v1/app-connections/zabbix/{connectionId}" +--- diff --git a/docs/api-reference/endpoints/app-connections/zabbix/get-by-name.mdx b/docs/api-reference/endpoints/app-connections/zabbix/get-by-name.mdx new file mode 100644 index 000000000..692c69fc7 --- /dev/null +++ b/docs/api-reference/endpoints/app-connections/zabbix/get-by-name.mdx @@ -0,0 +1,4 @@ +--- +title: "Get by Name" +openapi: "GET /api/v1/app-connections/zabbix/connection-name/{connectionName}" +--- diff --git a/docs/api-reference/endpoints/app-connections/zabbix/list.mdx b/docs/api-reference/endpoints/app-connections/zabbix/list.mdx new file mode 100644 index 000000000..bc1c7df2b --- /dev/null +++ b/docs/api-reference/endpoints/app-connections/zabbix/list.mdx @@ -0,0 +1,4 @@ +--- +title: "List" +openapi: "GET /api/v1/app-connections/zabbix" +--- diff --git a/docs/api-reference/endpoints/app-connections/zabbix/update.mdx b/docs/api-reference/endpoints/app-connections/zabbix/update.mdx new file mode 100644 index 000000000..aa408c9ee --- /dev/null +++ b/docs/api-reference/endpoints/app-connections/zabbix/update.mdx @@ -0,0 +1,8 @@ +--- +title: "Update" +openapi: "PATCH /api/v1/app-connections/zabbix/{connectionId}" +--- + + + Check out the configuration docs for [Zabbix Connections](/integrations/app-connections/zabbix) to learn how to obtain the required credentials. + diff --git a/docs/api-reference/endpoints/organizations/ldap-sso/create-ldap-config.mdx b/docs/api-reference/endpoints/organizations/ldap-sso/create-ldap-config.mdx new file mode 100644 index 000000000..3edabd366 --- /dev/null +++ b/docs/api-reference/endpoints/organizations/ldap-sso/create-ldap-config.mdx @@ -0,0 +1,4 @@ +--- +title: "Create LDAP SSO Config" +openapi: "POST /api/v1/ldap/config" +--- \ No newline at end of file diff --git a/docs/api-reference/endpoints/organizations/ldap-sso/get-ldap-config.mdx b/docs/api-reference/endpoints/organizations/ldap-sso/get-ldap-config.mdx new file mode 100644 index 000000000..a41669384 --- /dev/null +++ b/docs/api-reference/endpoints/organizations/ldap-sso/get-ldap-config.mdx @@ -0,0 +1,4 @@ +--- +title: "Get LDAP SSO Config" +openapi: "GET /api/v1/ldap/config" +--- \ No newline at end of file diff --git a/docs/api-reference/endpoints/organizations/ldap-sso/update-ldap-config.mdx b/docs/api-reference/endpoints/organizations/ldap-sso/update-ldap-config.mdx new file mode 100644 index 000000000..ab613728a --- /dev/null +++ b/docs/api-reference/endpoints/organizations/ldap-sso/update-ldap-config.mdx @@ -0,0 +1,4 @@ +--- +title: "Update LDAP SSO Config" +openapi: "PATCH /api/v1/ldap/config" +--- \ No newline at end of file diff --git a/docs/api-reference/endpoints/organizations/oidc-sso/create-oidc-config.mdx b/docs/api-reference/endpoints/organizations/oidc-sso/create-oidc-config.mdx new file mode 100644 index 000000000..6a86d970c --- /dev/null +++ b/docs/api-reference/endpoints/organizations/oidc-sso/create-oidc-config.mdx @@ -0,0 +1,4 @@ +--- +title: "Create OIDC Config" +openapi: "POST /api/v1/sso/oidc/config" +--- \ No newline at end of file diff --git a/docs/api-reference/endpoints/organizations/oidc-sso/get-oidc-config.mdx b/docs/api-reference/endpoints/organizations/oidc-sso/get-oidc-config.mdx new file mode 100644 index 000000000..af62c7d0a --- /dev/null +++ b/docs/api-reference/endpoints/organizations/oidc-sso/get-oidc-config.mdx @@ -0,0 +1,4 @@ +--- +title: "Get OIDC Config" +openapi: "GET /api/v1/sso/oidc/config" +--- \ No newline at end of file diff --git a/docs/api-reference/endpoints/organizations/oidc-sso/update-oidc-config.mdx b/docs/api-reference/endpoints/organizations/oidc-sso/update-oidc-config.mdx new file mode 100644 index 000000000..fdafeaf99 --- /dev/null +++ b/docs/api-reference/endpoints/organizations/oidc-sso/update-oidc-config.mdx @@ -0,0 +1,4 @@ +--- +title: "Update OIDC Config" +openapi: "PATCH /api/v1/sso/oidc/config" +--- \ No newline at end of file diff --git a/docs/api-reference/endpoints/organizations/saml-sso/create-saml-config.mdx b/docs/api-reference/endpoints/organizations/saml-sso/create-saml-config.mdx new file mode 100644 index 000000000..0a01fb742 --- /dev/null +++ b/docs/api-reference/endpoints/organizations/saml-sso/create-saml-config.mdx @@ -0,0 +1,4 @@ +--- +title: "Create SAML SSO Config" +openapi: "POST /api/v1/sso/config" +--- \ No newline at end of file diff --git a/docs/api-reference/endpoints/organizations/saml-sso/get-saml-config.mdx b/docs/api-reference/endpoints/organizations/saml-sso/get-saml-config.mdx new file mode 100644 index 000000000..71c00fb24 --- /dev/null +++ b/docs/api-reference/endpoints/organizations/saml-sso/get-saml-config.mdx @@ -0,0 +1,4 @@ +--- +title: "Get SAML SSO Config" +openapi: "GET /api/v1/sso/config" +--- \ No newline at end of file diff --git a/docs/api-reference/endpoints/organizations/saml-sso/update-saml-config.mdx b/docs/api-reference/endpoints/organizations/saml-sso/update-saml-config.mdx new file mode 100644 index 000000000..57067dbd1 --- /dev/null +++ b/docs/api-reference/endpoints/organizations/saml-sso/update-saml-config.mdx @@ -0,0 +1,4 @@ +--- +title: "Update SAML SSO Config" +openapi: "PATCH /api/v1/sso/config" +--- \ No newline at end of file diff --git a/docs/api-reference/endpoints/secret-syncs/zabbix/create.mdx b/docs/api-reference/endpoints/secret-syncs/zabbix/create.mdx new file mode 100644 index 000000000..1d15bd7d5 --- /dev/null +++ b/docs/api-reference/endpoints/secret-syncs/zabbix/create.mdx @@ -0,0 +1,4 @@ +--- +title: "Create" +openapi: "POST /api/v1/secret-syncs/zabbix" +--- diff --git a/docs/api-reference/endpoints/secret-syncs/zabbix/delete.mdx b/docs/api-reference/endpoints/secret-syncs/zabbix/delete.mdx new file mode 100644 index 000000000..dc7345298 --- /dev/null +++ b/docs/api-reference/endpoints/secret-syncs/zabbix/delete.mdx @@ -0,0 +1,4 @@ +--- +title: "Delete" +openapi: "DELETE /api/v1/secret-syncs/zabbix/{syncId}" +--- diff --git a/docs/api-reference/endpoints/secret-syncs/zabbix/get-by-id.mdx b/docs/api-reference/endpoints/secret-syncs/zabbix/get-by-id.mdx new file mode 100644 index 000000000..79e787d04 --- /dev/null +++ b/docs/api-reference/endpoints/secret-syncs/zabbix/get-by-id.mdx @@ -0,0 +1,4 @@ +--- +title: "Get by ID" +openapi: "GET /api/v1/secret-syncs/zabbix/{syncId}" +--- diff --git a/docs/api-reference/endpoints/secret-syncs/zabbix/get-by-name.mdx b/docs/api-reference/endpoints/secret-syncs/zabbix/get-by-name.mdx new file mode 100644 index 000000000..2b364c699 --- /dev/null +++ b/docs/api-reference/endpoints/secret-syncs/zabbix/get-by-name.mdx @@ -0,0 +1,4 @@ +--- +title: "Get by Name" +openapi: "GET /api/v1/secret-syncs/zabbix/sync-name/{syncName}" +--- diff --git a/docs/api-reference/endpoints/secret-syncs/zabbix/import-secrets.mdx b/docs/api-reference/endpoints/secret-syncs/zabbix/import-secrets.mdx new file mode 100644 index 000000000..716f3c8d5 --- /dev/null +++ b/docs/api-reference/endpoints/secret-syncs/zabbix/import-secrets.mdx @@ -0,0 +1,4 @@ +--- +title: "Import Secrets" +openapi: "POST /api/v1/secret-syncs/zabbix/{syncId}/import-secrets" +--- diff --git a/docs/api-reference/endpoints/secret-syncs/zabbix/list.mdx b/docs/api-reference/endpoints/secret-syncs/zabbix/list.mdx new file mode 100644 index 000000000..d6d4bdef5 --- /dev/null +++ b/docs/api-reference/endpoints/secret-syncs/zabbix/list.mdx @@ -0,0 +1,4 @@ +--- +title: "List" +openapi: "GET /api/v1/secret-syncs/zabbix" +--- diff --git a/docs/api-reference/endpoints/secret-syncs/zabbix/remove-secrets.mdx b/docs/api-reference/endpoints/secret-syncs/zabbix/remove-secrets.mdx new file mode 100644 index 000000000..8fb422544 --- /dev/null +++ b/docs/api-reference/endpoints/secret-syncs/zabbix/remove-secrets.mdx @@ -0,0 +1,4 @@ +--- +title: "Remove Secrets" +openapi: "POST /api/v1/secret-syncs/zabbix/{syncId}/remove-secrets" +--- diff --git a/docs/api-reference/endpoints/secret-syncs/zabbix/sync-secrets.mdx b/docs/api-reference/endpoints/secret-syncs/zabbix/sync-secrets.mdx new file mode 100644 index 000000000..9751ad721 --- /dev/null +++ b/docs/api-reference/endpoints/secret-syncs/zabbix/sync-secrets.mdx @@ -0,0 +1,4 @@ +--- +title: "Sync Secrets" +openapi: "POST /api/v1/secret-syncs/zabbix/{syncId}/sync-secrets" +--- diff --git a/docs/api-reference/endpoints/secret-syncs/zabbix/update.mdx b/docs/api-reference/endpoints/secret-syncs/zabbix/update.mdx new file mode 100644 index 000000000..ea7582143 --- /dev/null +++ b/docs/api-reference/endpoints/secret-syncs/zabbix/update.mdx @@ -0,0 +1,4 @@ +--- +title: "Update" +openapi: "PATCH /api/v1/secret-syncs/zabbix/{syncId}" +--- diff --git a/docs/docs.json b/docs/docs.json index ac9d62a7c..2aa545375 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -490,7 +490,8 @@ "integrations/app-connections/teamcity", "integrations/app-connections/terraform-cloud", "integrations/app-connections/vercel", - "integrations/app-connections/windmill" + "integrations/app-connections/windmill", + "integrations/app-connections/zabbix" ] } ] @@ -523,7 +524,8 @@ "integrations/secret-syncs/teamcity", "integrations/secret-syncs/terraform-cloud", "integrations/secret-syncs/vercel", - "integrations/secret-syncs/windmill" + "integrations/secret-syncs/windmill", + "integrations/secret-syncs/zabbix" ] } ] @@ -849,6 +851,30 @@ { "group": "Organizations", "pages": [ + { + "group": "OIDC SSO", + "pages": [ + "api-reference/endpoints/organizations/oidc-sso/get-oidc-config", + "api-reference/endpoints/organizations/oidc-sso/update-oidc-config", + "api-reference/endpoints/organizations/oidc-sso/create-oidc-config" + ] + }, + { + "group": "LDAP SSO", + "pages": [ + "api-reference/endpoints/organizations/ldap-sso/get-ldap-config", + "api-reference/endpoints/organizations/ldap-sso/update-ldap-config", + "api-reference/endpoints/organizations/ldap-sso/create-ldap-config" + ] + }, + { + "group": "SAML SSO", + "pages": [ + "api-reference/endpoints/organizations/saml-sso/get-saml-config", + "api-reference/endpoints/organizations/saml-sso/update-saml-config", + "api-reference/endpoints/organizations/saml-sso/create-saml-config" + ] + }, "api-reference/endpoints/organizations/memberships", "api-reference/endpoints/organizations/update-membership", "api-reference/endpoints/organizations/delete-membership", @@ -1521,6 +1547,18 @@ "api-reference/endpoints/app-connections/windmill/update", "api-reference/endpoints/app-connections/windmill/delete" ] + }, + { + "group": "Zabbix", + "pages": [ + "api-reference/endpoints/app-connections/zabbix/list", + "api-reference/endpoints/app-connections/zabbix/available", + "api-reference/endpoints/app-connections/zabbix/get-by-id", + "api-reference/endpoints/app-connections/zabbix/get-by-name", + "api-reference/endpoints/app-connections/zabbix/create", + "api-reference/endpoints/app-connections/zabbix/update", + "api-reference/endpoints/app-connections/zabbix/delete" + ] } ] }, @@ -1827,6 +1865,20 @@ "api-reference/endpoints/secret-syncs/windmill/import-secrets", "api-reference/endpoints/secret-syncs/windmill/remove-secrets" ] + }, + { + "group": "Zabbix", + "pages": [ + "api-reference/endpoints/secret-syncs/zabbix/list", + "api-reference/endpoints/secret-syncs/zabbix/get-by-id", + "api-reference/endpoints/secret-syncs/zabbix/get-by-name", + "api-reference/endpoints/secret-syncs/zabbix/create", + "api-reference/endpoints/secret-syncs/zabbix/update", + "api-reference/endpoints/secret-syncs/zabbix/delete", + "api-reference/endpoints/secret-syncs/zabbix/sync-secrets", + "api-reference/endpoints/secret-syncs/zabbix/import-secrets", + "api-reference/endpoints/secret-syncs/zabbix/remove-secrets" + ] } ] }, diff --git a/docs/images/app-connections/zabbix/zabbix-api-token-form.png b/docs/images/app-connections/zabbix/zabbix-api-token-form.png new file mode 100644 index 000000000..471ecc4a4 Binary files /dev/null and b/docs/images/app-connections/zabbix/zabbix-api-token-form.png differ diff --git a/docs/images/app-connections/zabbix/zabbix-api-token-generated.png b/docs/images/app-connections/zabbix/zabbix-api-token-generated.png new file mode 100644 index 000000000..f05dd279e Binary files /dev/null and b/docs/images/app-connections/zabbix/zabbix-api-token-generated.png differ diff --git a/docs/images/app-connections/zabbix/zabbix-api-token-list.png b/docs/images/app-connections/zabbix/zabbix-api-token-list.png new file mode 100644 index 000000000..d549cc576 Binary files /dev/null and b/docs/images/app-connections/zabbix/zabbix-api-token-list.png differ diff --git a/docs/images/app-connections/zabbix/zabbix-app-connection-form.png b/docs/images/app-connections/zabbix/zabbix-app-connection-form.png new file mode 100644 index 000000000..90ac10f5f Binary files /dev/null and b/docs/images/app-connections/zabbix/zabbix-app-connection-form.png differ diff --git a/docs/images/app-connections/zabbix/zabbix-app-connection-generated.png b/docs/images/app-connections/zabbix/zabbix-app-connection-generated.png new file mode 100644 index 000000000..87cf99a20 Binary files /dev/null and b/docs/images/app-connections/zabbix/zabbix-app-connection-generated.png differ diff --git a/docs/images/app-connections/zabbix/zabbix-app-connection-option.png b/docs/images/app-connections/zabbix/zabbix-app-connection-option.png new file mode 100644 index 000000000..4cd01571c Binary files /dev/null and b/docs/images/app-connections/zabbix/zabbix-app-connection-option.png differ diff --git a/docs/images/app-connections/zabbix/zabbix-dashboard.png b/docs/images/app-connections/zabbix/zabbix-dashboard.png new file mode 100644 index 000000000..1f80f2e1b Binary files /dev/null and b/docs/images/app-connections/zabbix/zabbix-dashboard.png differ diff --git a/docs/images/secret-syncs/zabbix/configure-destination.png b/docs/images/secret-syncs/zabbix/configure-destination.png new file mode 100644 index 000000000..deb9ad993 Binary files /dev/null and b/docs/images/secret-syncs/zabbix/configure-destination.png differ diff --git a/docs/images/secret-syncs/zabbix/configure-details.png b/docs/images/secret-syncs/zabbix/configure-details.png new file mode 100644 index 000000000..c454de858 Binary files /dev/null and b/docs/images/secret-syncs/zabbix/configure-details.png differ diff --git a/docs/images/secret-syncs/zabbix/configure-source.png b/docs/images/secret-syncs/zabbix/configure-source.png new file mode 100644 index 000000000..82b2630b4 Binary files /dev/null and b/docs/images/secret-syncs/zabbix/configure-source.png differ diff --git a/docs/images/secret-syncs/zabbix/configure-sync-options.png b/docs/images/secret-syncs/zabbix/configure-sync-options.png new file mode 100644 index 000000000..ad54ce8ad Binary files /dev/null and b/docs/images/secret-syncs/zabbix/configure-sync-options.png differ diff --git a/docs/images/secret-syncs/zabbix/review-configuration.png b/docs/images/secret-syncs/zabbix/review-configuration.png new file mode 100644 index 000000000..25b0fbf8f Binary files /dev/null and b/docs/images/secret-syncs/zabbix/review-configuration.png differ diff --git a/docs/images/secret-syncs/zabbix/select-option.png b/docs/images/secret-syncs/zabbix/select-option.png new file mode 100644 index 000000000..9ebf248a0 Binary files /dev/null and b/docs/images/secret-syncs/zabbix/select-option.png differ diff --git a/docs/images/secret-syncs/zabbix/sync-created.png b/docs/images/secret-syncs/zabbix/sync-created.png new file mode 100644 index 000000000..e9924c82f Binary files /dev/null and b/docs/images/secret-syncs/zabbix/sync-created.png differ diff --git a/docs/images/self-hosting/configuration/overrides/page.png b/docs/images/self-hosting/configuration/overrides/page.png new file mode 100644 index 000000000..660273d4f Binary files /dev/null and b/docs/images/self-hosting/configuration/overrides/page.png differ diff --git a/docs/integrations/app-connections/zabbix.mdx b/docs/integrations/app-connections/zabbix.mdx new file mode 100644 index 000000000..c4d47b22e --- /dev/null +++ b/docs/integrations/app-connections/zabbix.mdx @@ -0,0 +1,101 @@ +--- +title: "Zabbix Connection" +description: "Learn how to configure a Zabbix Connection for Infisical." +--- + +Infisical supports the use of [API Tokens](https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/users/api_tokens) to connect with Zabbix. + +## Create Zabbix API Token + + + + ![Dashboard Page](/images/app-connections/zabbix/zabbix-dashboard.png) + + + ![Click Create Token](/images/app-connections/zabbix/zabbix-api-token-list.png) + + + Ensure that you give this token access to the correct app, then click 'Create Token'. + + ![Create Token Page](/images/app-connections/zabbix/zabbix-api-token-form.png) + + + After clicking 'Create Token', a modal containing your access token will appear. Save this token for later steps. + ![Copy Token Modal](/images/app-connections/zabbix/zabbix-api-token-generated.png) + + + +## Create Zabbix Connection in Infisical + + + + + + In your Infisical dashboard, go to **Organization Settings** and select the [**App Connections**](https://app.infisical.com/organization/app-connections) tab. + + ![App Connections Tab](/images/app-connections/general/add-connection.png) + + + Click the **+ Add Connection** button and select the **Zabbix Connection** option from the available integrations. + + ![Select Zabbix Connection](/images/app-connections/zabbix/zabbix-app-connection-option.png) + + + Complete the Zabbix Connection form by entering: + - A descriptive name for the connection + - An optional description for future reference + - The Zabbix URL for your instance + - The API Token from earlier steps + + ![Zabbix Connection Modal](/images/app-connections/zabbix/zabbix-app-connection-form.png) + + + After clicking Create, your **Zabbix Connection** is established and ready to use with your Infisical projects. + + ![Zabbix Connection Created](/images/app-connections/zabbix/zabbix-app-connection-generated.png) + + + + + To create a Zabbix Connection, make an API request to the [Create Zabbix Connection](/api-reference/endpoints/app-connections/zabbix/create) API endpoint. + + ### Sample request + + ```bash Request + curl --request POST \ + --url https://app.infisical.com/api/v1/app-connections/zabbix \ + --header 'Content-Type: application/json' \ + --data '{ + "name": "my-zabbix-connection", + "method": "api-token", + "credentials": { + "apiToken": "[API TOKEN]", + "instanceUrl": "https://zabbix.example.com" + } + }' + ``` + + ### Sample response + + ```bash Response + { + "appConnection": { + "id": "e5d18aca-86f7-4026-a95e-efb8aeb0d8e6", + "name": "my-zabbix-connection", + "description": null, + "version": 1, + "orgId": "6f03caa1-a5de-43ce-b127-95a145d3464c", + "createdAt": "2025-04-23T19:46:34.831Z", + "updatedAt": "2025-04-23T19:46:34.831Z", + "isPlatformManagedCredentials": false, + "credentialsHash": "7c2d371dec195f82a6a0d5b41c970a229cfcaf88e894a5b6395e2dbd0280661f", + "app": "zabbix", + "method": "api-token", + "credentials": { + "instanceUrl": "https://zabbix.example.com" + } + } + } + ``` + + diff --git a/docs/integrations/secret-syncs/zabbix.mdx b/docs/integrations/secret-syncs/zabbix.mdx new file mode 100644 index 000000000..bb3292069 --- /dev/null +++ b/docs/integrations/secret-syncs/zabbix.mdx @@ -0,0 +1,173 @@ +--- +title: "Zabbix Sync" +description: "Learn how to configure a Zabbix Sync for Infisical." +--- + +**Prerequisites:** +- Create a [Zabbix Connection](/integrations/app-connections/zabbix) + + + + + + Navigate to **Project** > **Integrations** and select the **Secret Syncs** tab. Click on the **Add Sync** button. + + ![Secret Syncs Tab](/images/secret-syncs/general/secret-sync-tab.png) + + + ![Select Zabbix](/images/secret-syncs/zabbix/select-option.png) + + + Configure the **Source** from where secrets should be retrieved, then click **Next**. + + ![Configure Source](/images/secret-syncs/zabbix/configure-source.png) + + - **Environment**: The project environment to retrieve secrets from. + - **Secret Path**: The folder path to retrieve secrets from. + + + If you need to sync secrets from multiple folder locations, check out [secret imports](/documentation/platform/secret-reference#secret-imports). + + + + Configure the **Destination** to where secrets should be deployed, then click **Next**. + + ![Configure Destination](/images/secret-syncs/zabbix/configure-destination.png) + + - **Zabbix Connection**: The Zabbix Connection to authenticate with. + - **Scope**: The Zabbix scope to sync secrets to. + - **Global**: Secrets will be synced globally. + - **Host**: Secrets will be synced to the specified host. + - **Macro Type**: The type of macro to use when syncing secrets to Zabbix. Currently only **Text** and **Secret** macros are supported. + The remaining fields are determined by the selected **Scope**: + + + - **Host**: The host to sync secrets to. + + + + + Configure the **Sync Options** to specify how secrets should be synced, then click **Next**. + + ![Configure Options](/images/secret-syncs/zabbix/configure-sync-options.png) + + - **Initial Sync Behavior**: Determines how Infisical should resolve the initial sync. + - **Overwrite Destination Secrets**: Removes any secrets at the destination endpoint not present in Infisical. + - **Import Secrets (Prioritize Infisical)**: Imports secrets from the destination endpoint before syncing, prioritizing values from Infisical over Zabbix when keys conflict. + - **Import Secrets (Prioritize Zabbix)**: Imports secrets from the destination endpoint before syncing, prioritizing values from Zabbix over Infisical when keys conflict. + - **Key Schema**: Template that determines how secret names are transformed when syncing, using `{{secretKey}}` as a placeholder for the original secret name and `{{environment}}` for the environment. + + We highly recommend using a Key Schema to ensure that Infisical only manages the specific keys you intend, keeping everything else untouched. + + - **Auto-Sync Enabled**: If enabled, secrets will automatically be synced from the source location when changes occur. Disable to enforce manual syncing only. + - **Disable Secret Deletion**: If enabled, Infisical will not remove secrets from the sync destination. Enable this option if you intend to manage some secrets manually outside of Infisical. + + + Configure the **Details** of your Zabbix Sync, then click **Next**. + + ![Configure Details](/images/secret-syncs/zabbix/configure-details.png) + + - **Name**: The name of your sync. Must be slug-friendly. + - **Description**: An optional description for your sync. + + + Review your Zabbix Sync configuration, then click **Create Sync**. + + ![Review Configuration](/images/secret-syncs/zabbix/review-configuration.png) + + + If enabled, your Zabbix Sync will begin syncing your secrets to the destination endpoint. + + ![Sync Created](/images/secret-syncs/zabbix/sync-created.png) + + + + + To create a **Zabbix Sync**, make an API request to the [Create Zabbix Sync](/api-reference/endpoints/secret-syncs/zabbix/create) API endpoint. + + ### Sample request + + ```bash Request + curl --request POST \ + --url https://app.infisical.com/api/v1/secret-syncs/zabbix \ + --header 'Content-Type: application/json' \ + --data '{ + "name": "my-zabbix-sync", + "projectId": "3c90c3cc-0d44-4b50-8888-8dd25736052a", + "description": "an example sync", + "connectionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a", + "environment": "dev", + "secretPath": "/my-secrets", + "isEnabled": true, + "syncOptions": { + "initialSyncBehavior": "overwrite-destination", + "autoSyncEnabled": true, + "disableSecretDeletion": false + }, + "destinationConfig": { + "scope": "host", + "hostId": "my-zabbix-host", + "hostName": "my-zabbix-host", + "macroType": 0 + } + }' + ``` + + ### Sample response + + ```bash Response + { + "secretSync": { + "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a", + "name": "my-zabbix-sync", + "description": "an example sync", + "isEnabled": true, + "version": 1, + "folderId": "3c90c3cc-0d44-4b50-8888-8dd25736052a", + "connectionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a", + "createdAt": "2023-11-07T05:31:56Z", + "updatedAt": "2023-11-07T05:31:56Z", + "syncStatus": "succeeded", + "lastSyncJobId": "123", + "lastSyncMessage": null, + "lastSyncedAt": "2023-11-07T05:31:56Z", + "importStatus": null, + "lastImportJobId": null, + "lastImportMessage": null, + "lastImportedAt": null, + "removeStatus": null, + "lastRemoveJobId": null, + "lastRemoveMessage": null, + "lastRemovedAt": null, + "syncOptions": { + "initialSyncBehavior": "overwrite-destination", + "autoSyncEnabled": true, + "disableSecretDeletion": false + }, + "projectId": "3c90c3cc-0d44-4b50-8888-8dd25736052a", + "connection": { + "app": "zabbix", + "name": "my-zabbix-connection", + "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a" + }, + "environment": { + "slug": "dev", + "name": "Development", + "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a" + }, + "folder": { + "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a", + "path": "/my-secrets" + }, + "destination": "zabbix", + "destinationConfig": { + "scope": "host", + "hostId": "my-zabbix-host", + "hostName": "my-zabbix-host", + "macroType": 0 + } + } + } + ``` + + diff --git a/docs/mint.json b/docs/mint.json deleted file mode 100644 index 2dcb233d3..000000000 --- a/docs/mint.json +++ /dev/null @@ -1,2241 +0,0 @@ -{ - "name": "Infisical", - "openapi": "https://app.infisical.com/api/docs/json", - "logo": { - "dark": "/logo/dark.svg", - "light": "/logo/light.svg", - "href": "https://infisical.com" - }, - "favicon": "/favicon.png", - "colors": { - "primary": "#26272b", - "light": "#97b31d", - "dark": "#A1B659", - "ultraLight": "#E7F256", - "ultraDark": "#8D9F4C", - "background": { - "light": "#ffffff", - "dark": "#0D1117" - }, - "anchors": { - "from": "#000000", - "to": "#707174" - } - }, - "modeToggle": { - "default": "light", - "isHidden": true - }, - "feedback": { - "suggestEdit": true, - "raiseIssue": true, - "thumbsRating": true - }, - "api": { - "baseUrl": ["https://app.infisical.com", "http://localhost:8080"] - }, - "topbarLinks": [ - { - "name": "Log In", - "url": "https://app.infisical.com/login" - } - ], - "topbarCtaButton": { - "name": "Start for Free", - "url": "https://app.infisical.com/signup" - }, - "tabs": [ - { - "name": "Integrations", - "url": "integrations" - }, - { - "name": "CLI", - "url": "cli" - }, - { - "name": "API Reference", - "url": "api-reference" - }, - { - "name": "SDKs", - "url": "sdks" - }, - { - "name": "Changelog", - "url": "changelog" - } - ], - "navigation": [ - { - "group": "Getting Started", - "pages": [ - "documentation/getting-started/introduction", - { - "group": "Quickstart", - "pages": ["documentation/guides/local-development"] - }, - { - "group": "Guides", - "pages": [ - "documentation/guides/introduction", - "documentation/guides/node", - "documentation/guides/python", - "documentation/guides/nextjs-vercel", - "documentation/guides/microsoft-power-apps", - "documentation/guides/organization-structure" - ] - }, - { - "group": "Setup", - "pages": ["documentation/setup/networking"] - } - ] - }, - { - "group": "Platform", - "pages": [ - "documentation/platform/organization", - "documentation/platform/project", - "documentation/platform/folder", - { - "group": "Secrets", - "pages": [ - "documentation/platform/secret-versioning", - "documentation/platform/pit-recovery", - "documentation/platform/secret-reference", - "documentation/platform/webhooks" - ] - }, - { - "group": "Internal PKI", - "pages": [ - "documentation/platform/pki/overview", - "documentation/platform/pki/private-ca", - "documentation/platform/pki/external-ca", - "documentation/platform/pki/subscribers", - "documentation/platform/pki/certificates", - "documentation/platform/pki/acme-ca", - "documentation/platform/pki/est", - "documentation/platform/pki/alerting", - { - "group": "Integrations", - "pages": [ - "documentation/platform/pki/pki-issuer", - "documentation/platform/pki/integration-guides/gloo-mesh" - ] - } - ] - }, - { - "group": "Infisical SSH", - "pages": [ - "documentation/platform/ssh/overview", - "documentation/platform/ssh/host-groups" - ] - }, - { - "group": "Key Management (KMS)", - "pages": [ - "documentation/platform/kms/overview", - "documentation/platform/kms/hsm-integration", - "documentation/platform/kms/kubernetes-encryption", - "documentation/platform/kms/kmip" - ] - }, - { - "group": "KMS Configuration", - "pages": [ - "documentation/platform/kms-configuration/overview", - "documentation/platform/kms-configuration/aws-kms", - "documentation/platform/kms-configuration/aws-hsm", - "documentation/platform/kms-configuration/gcp-kms" - ] - }, - { - "group": "Identities", - "pages": [ - "documentation/platform/identities/overview", - "documentation/platform/identities/user-identities", - "documentation/platform/identities/machine-identities" - ] - }, - { - "group": "Access Control", - "pages": [ - "documentation/platform/access-controls/overview", - "documentation/platform/access-controls/role-based-access-controls", - { - "group": "Attribute based access controls", - "pages": [ - "documentation/platform/access-controls/abac/overview", - "documentation/platform/access-controls/abac/managing-user-metadata", - "documentation/platform/access-controls/abac/managing-machine-identity-attributes" - ] - }, - "documentation/platform/access-controls/additional-privileges", - "documentation/platform/access-controls/temporary-access", - "documentation/platform/access-controls/assume-privilege", - "documentation/platform/access-controls/access-requests", - "documentation/platform/access-controls/project-access-requests", - "documentation/platform/pr-workflows", - "documentation/platform/groups" - ] - }, - { - "group": "Audit Logs", - "pages": [ - "documentation/platform/audit-logs", - "documentation/platform/audit-log-streams/audit-log-streams", - "documentation/platform/audit-log-streams/audit-log-streams-with-fluentbit" - ] - }, - { - "group": "Secret Rotation", - "pages": [ - "documentation/platform/secret-rotation/overview", - "documentation/platform/secret-rotation/auth0-client-secret", - "documentation/platform/secret-rotation/aws-iam-user-secret", - "documentation/platform/secret-rotation/azure-client-secret", - "documentation/platform/secret-rotation/ldap-password", - "documentation/platform/secret-rotation/mssql-credentials", - "documentation/platform/secret-rotation/mysql-credentials", - "documentation/platform/secret-rotation/oracledb-credentials", - "documentation/platform/secret-rotation/postgres-credentials" - ] - }, - { - "group": "Dynamic Secrets", - "pages": [ - "documentation/platform/dynamic-secrets/overview", - "documentation/platform/dynamic-secrets/aws-elasticache", - "documentation/platform/dynamic-secrets/aws-iam", - "documentation/platform/dynamic-secrets/azure-entra-id", - "documentation/platform/dynamic-secrets/cassandra", - "documentation/platform/dynamic-secrets/elastic-search", - "documentation/platform/dynamic-secrets/gcp-iam", - "documentation/platform/dynamic-secrets/ldap", - "documentation/platform/dynamic-secrets/mongo-atlas", - "documentation/platform/dynamic-secrets/mongo-db", - "documentation/platform/dynamic-secrets/mssql", - "documentation/platform/dynamic-secrets/mysql", - "documentation/platform/dynamic-secrets/oracle", - "documentation/platform/dynamic-secrets/postgresql", - "documentation/platform/dynamic-secrets/rabbit-mq", - "documentation/platform/dynamic-secrets/redis", - "documentation/platform/dynamic-secrets/sap-ase", - "documentation/platform/dynamic-secrets/sap-hana", - "documentation/platform/dynamic-secrets/snowflake", - "documentation/platform/dynamic-secrets/totp", - "documentation/platform/dynamic-secrets/kubernetes", - "documentation/platform/dynamic-secrets/vertica" - ] - }, - { - "group": "Gateway", - "pages": [ - "documentation/platform/gateways/overview", - "documentation/platform/gateways/gateway-security", - "documentation/platform/gateways/networking" - ] - }, - "documentation/platform/project-templates", - { - "group": "Workflow Integrations", - "pages": [ - "documentation/platform/workflow-integrations/slack-integration", - "documentation/platform/workflow-integrations/microsoft-teams-integration" - ] - }, - { - "group": "Admin Consoles", - "pages": [ - "documentation/platform/admin-panel/overview", - "documentation/platform/admin-panel/server-admin", - "documentation/platform/admin-panel/org-admin-console" - ] - }, - "documentation/platform/secret-sharing", - { - "group": "Secret Scanning", - "pages": [ - "documentation/platform/secret-scanning/overview", - "documentation/platform/secret-scanning/github" - ] - } - ] - }, - { - "group": "Authentication Methods", - "pages": [ - { - "group": "User Authentication", - "pages": [ - "documentation/platform/auth-methods/email-password", - { - "group": "SSO", - "pages": [ - "documentation/platform/sso/overview", - "documentation/platform/sso/google", - "documentation/platform/sso/github", - "documentation/platform/sso/gitlab", - "documentation/platform/sso/okta", - "documentation/platform/sso/azure", - "documentation/platform/sso/jumpcloud", - "documentation/platform/sso/keycloak-saml", - "documentation/platform/sso/google-saml", - "documentation/platform/sso/auth0-saml", - { - "group": "OIDC", - "pages": [ - { - "group": "Keycloak OIDC", - "pages": [ - "documentation/platform/sso/keycloak-oidc/overview", - "documentation/platform/sso/keycloak-oidc/group-membership-mapping" - ] - }, - "documentation/platform/sso/auth0-oidc", - { - "group": "General OIDC", - "pages": [ - "documentation/platform/sso/general-oidc/overview", - "documentation/platform/sso/general-oidc/group-membership-mapping" - ] - } - ] - } - ] - }, - { - "group": "LDAP", - "pages": [ - "documentation/platform/ldap/overview", - "documentation/platform/ldap/jumpcloud", - "documentation/platform/ldap/general" - ] - }, - { - "group": "SCIM", - "pages": [ - "documentation/platform/scim/overview", - "documentation/platform/scim/okta", - "documentation/platform/scim/azure", - "documentation/platform/scim/jumpcloud", - "documentation/platform/scim/group-mappings" - ] - } - ] - }, - - { - "group": "Machine Identities", - "pages": [ - "documentation/platform/identities/alicloud-auth", - "documentation/platform/identities/aws-auth", - "documentation/platform/identities/azure-auth", - "documentation/platform/identities/gcp-auth", - "documentation/platform/identities/jwt-auth", - "documentation/platform/identities/kubernetes-auth", - "documentation/platform/identities/oci-auth", - "documentation/platform/identities/token-auth", - "documentation/platform/identities/universal-auth", - { - "group": "OIDC Auth", - "pages": [ - "documentation/platform/identities/oidc-auth/general", - "documentation/platform/identities/oidc-auth/azure", - "documentation/platform/identities/oidc-auth/github", - "documentation/platform/identities/oidc-auth/circleci", - "documentation/platform/identities/oidc-auth/gitlab", - "documentation/platform/identities/oidc-auth/terraform-cloud", - "documentation/platform/identities/oidc-auth/spire" - ] - }, - - { - "group": "LDAP Auth", - "pages": [ - "documentation/platform/identities/ldap-auth/general", - "documentation/platform/identities/ldap-auth/jumpcloud" - ] - } - ] - }, - "documentation/platform/token", - "documentation/platform/mfa", - "documentation/platform/github-org-sync" - ] - }, - { - "group": "Self-host Infisical", - "pages": [ - "self-hosting/overview", - { - "group": "Installation methods", - "pages": [ - "self-hosting/deployment-options/standalone-infisical", - "self-hosting/deployment-options/docker-swarm", - "self-hosting/deployment-options/docker-compose", - "self-hosting/deployment-options/kubernetes-helm" - ] - }, - { - "group": "Linux Package", - "pages": [ - "self-hosting/deployment-options/native/linux-package/installation", - "self-hosting/deployment-options/native/linux-package/commands-configuration", - "self-hosting/deployment-options/linux-upgrade" - ] - }, - "self-hosting/guides/upgrading-infisical", - "self-hosting/configuration/envars", - "self-hosting/configuration/requirements", - { - "group": "Guides", - "pages": [ - "self-hosting/guides/mongo-to-postgres", - "self-hosting/guides/custom-certificates", - "self-hosting/guides/automated-bootstrapping", - "self-hosting/guides/production-hardening" - ] - }, - { - "group": "Reference architectures", - "pages": [ - "self-hosting/reference-architectures/aws-ecs", - "self-hosting/reference-architectures/linux-deployment-ha", - "self-hosting/reference-architectures/on-prem-k8s-ha", - "self-hosting/reference-architectures/google-cloud-run" - ] - }, - "self-hosting/ee", - "self-hosting/faq" - ] - }, - { - "group": "Command line", - "pages": [ - "cli/overview", - "cli/usage", - { - "group": "Core commands", - "pages": [ - "cli/commands/login", - "cli/commands/init", - "cli/commands/run", - "cli/commands/secrets", - "cli/commands/dynamic-secrets", - "cli/commands/ssh", - "cli/commands/gateway", - "cli/commands/bootstrap", - "cli/commands/export", - "cli/commands/token", - "cli/commands/service-token", - "cli/commands/vault", - "cli/commands/user", - "cli/commands/reset", - { - "group": "infisical scan", - "pages": [ - "cli/commands/scan", - "cli/commands/scan-git-changes", - "cli/commands/scan-install" - ] - } - ] - }, - "cli/scanning-overview", - "cli/project-config", - "cli/faq" - ] - }, - { - "group": "Infrastructure Integrations", - "pages": [ - "integrations/platforms/ansible", - "integrations/platforms/apache-airflow", - { - "group": "Container orchestrators", - "pages": [ - { - "group": "Kubernetes", - "pages": [ - "integrations/platforms/kubernetes/overview", - "integrations/platforms/kubernetes/infisical-secret-crd", - "integrations/platforms/kubernetes/infisical-push-secret-crd", - "integrations/platforms/kubernetes/infisical-dynamic-secret-crd" - ] - }, - "integrations/platforms/kubernetes-injector", - "integrations/platforms/kubernetes-csi", - "integrations/platforms/docker-swarm-with-agent", - "integrations/platforms/ecs-with-agent" - ] - }, - { - "group": "Docker", - "pages": [ - "integrations/platforms/docker-intro", - "integrations/platforms/docker", - "integrations/platforms/docker-pass-envs", - "integrations/platforms/docker-compose" - ] - }, - "integrations/platforms/infisical-agent", - "integrations/frameworks/packer", - "integrations/frameworks/pulumi", - "integrations/frameworks/terraform" - ] - }, - { - "group": "App Connections", - "pages": [ - "integrations/app-connections/overview", - { - "group": "Connections", - "pages": [ - "integrations/app-connections/1password", - "integrations/app-connections/auth0", - "integrations/app-connections/aws", - "integrations/app-connections/azure-app-configuration", - "integrations/app-connections/azure-client-secrets", - "integrations/app-connections/azure-devops", - "integrations/app-connections/azure-key-vault", - "integrations/app-connections/camunda", - "integrations/app-connections/databricks", - "integrations/app-connections/flyio", - "integrations/app-connections/gcp", - "integrations/app-connections/github", - "integrations/app-connections/github-radar", - "integrations/app-connections/gitlab", - "integrations/app-connections/hashicorp-vault", - "integrations/app-connections/heroku", - "integrations/app-connections/humanitec", - "integrations/app-connections/ldap", - "integrations/app-connections/mssql", - "integrations/app-connections/mysql", - "integrations/app-connections/oci", - "integrations/app-connections/oracledb", - "integrations/app-connections/postgres", - "integrations/app-connections/render", - "integrations/app-connections/teamcity", - "integrations/app-connections/terraform-cloud", - "integrations/app-connections/vercel", - "integrations/app-connections/windmill" - ] - } - ] - }, - { - "group": "Secret Syncs", - "pages": [ - "integrations/secret-syncs/overview", - { - "group": "Syncs", - "pages": [ - "integrations/secret-syncs/1password", - "integrations/secret-syncs/aws-parameter-store", - "integrations/secret-syncs/aws-secrets-manager", - "integrations/secret-syncs/azure-app-configuration", - "integrations/secret-syncs/azure-devops", - "integrations/secret-syncs/azure-key-vault", - "integrations/secret-syncs/camunda", - "integrations/secret-syncs/databricks", - "integrations/secret-syncs/flyio", - "integrations/secret-syncs/gcp-secret-manager", - "integrations/secret-syncs/github", - "integrations/secret-syncs/gitlab", - "integrations/secret-syncs/hashicorp-vault", - "integrations/secret-syncs/heroku", - "integrations/secret-syncs/humanitec", - "integrations/secret-syncs/oci-vault", - "integrations/secret-syncs/render", - "integrations/secret-syncs/teamcity", - "integrations/secret-syncs/terraform-cloud", - "integrations/secret-syncs/vercel", - "integrations/secret-syncs/windmill" - ] - } - ] - }, - { - "group": "Native Integrations", - "pages": [ - { - "group": "AWS", - "pages": [ - "integrations/cloud/aws-parameter-store", - "integrations/cloud/aws-secret-manager", - "integrations/cloud/aws-amplify" - ] - }, - "integrations/cloud/vercel", - "integrations/cloud/azure-key-vault", - "integrations/cloud/azure-app-configuration", - "integrations/cloud/azure-devops", - "integrations/cloud/gcp-secret-manager", - { - "group": "Cloudflare", - "pages": [ - "integrations/cloud/cloudflare-pages", - "integrations/cloud/cloudflare-workers" - ] - }, - "integrations/cloud/terraform-cloud", - "integrations/cloud/databricks", - { - "group": "View more", - "pages": [ - "integrations/cloud/digital-ocean-app-platform", - "integrations/cloud/heroku", - "integrations/cloud/netlify", - "integrations/cloud/railway", - "integrations/cloud/flyio", - "integrations/cloud/render", - "integrations/cloud/laravel-forge", - "integrations/cloud/supabase", - "integrations/cloud/northflank", - "integrations/cloud/hasura-cloud", - "integrations/cloud/qovery", - "integrations/cloud/hashicorp-vault", - "integrations/cloud/cloud-66", - "integrations/cloud/windmill" - ] - } - ] - }, - { - "group": "CI/CD Integrations", - "pages": [ - "integrations/cicd/jenkins", - "integrations/cicd/githubactions", - "integrations/cicd/gitlab", - "integrations/cicd/bitbucket", - "integrations/cloud/teamcity", - { - "group": "View more", - "pages": [ - "integrations/cicd/circleci", - "integrations/cicd/travisci", - "integrations/cicd/rundeck", - "integrations/cicd/codefresh", - "integrations/cloud/checkly", - "integrations/cicd/octopus-deploy" - ] - } - ] - }, - { - "group": "Framework Integrations", - "pages": [ - "integrations/frameworks/spring-boot-maven", - "integrations/frameworks/react", - "integrations/frameworks/vue", - "integrations/frameworks/express", - { - "group": "View more", - "pages": [ - "integrations/frameworks/nextjs", - "integrations/frameworks/nestjs", - "integrations/frameworks/sveltekit", - "integrations/frameworks/nuxt", - "integrations/frameworks/gatsby", - "integrations/frameworks/remix", - "integrations/frameworks/vite", - "integrations/frameworks/fiber", - "integrations/frameworks/django", - "integrations/frameworks/flask", - "integrations/frameworks/laravel", - "integrations/frameworks/rails", - "integrations/frameworks/dotnet", - "integrations/platforms/pm2", - "integrations/frameworks/ab-initio" - ] - } - ] - }, - { - "group": "Build Tool Integrations", - "pages": ["integrations/build-tools/gradle"] - }, - { - "group": "Others", - "pages": ["integrations/external/backstage"] - }, - { - "group": "", - "pages": ["sdks/overview"] - }, - { - "group": "SDK's", - "pages": [ - "sdks/languages/node", - "sdks/languages/python", - "sdks/languages/java", - "sdks/languages/csharp", - "sdks/languages/go", - "sdks/languages/ruby" - ] - }, - { - "group": "Overview", - "pages": [ - "api-reference/overview/introduction", - "api-reference/overview/authentication", - { - "group": "Examples", - "pages": ["api-reference/overview/examples/integration"] - } - ] - }, - { - "group": "Endpoints", - "pages": [ - { - "group": "Identities", - "pages": [ - "api-reference/endpoints/identities/create", - "api-reference/endpoints/identities/update", - "api-reference/endpoints/identities/delete", - "api-reference/endpoints/identities/get-by-id", - "api-reference/endpoints/identities/list", - "api-reference/endpoints/identities/search" - ] - }, - { - "group": "Token Auth", - "pages": [ - "api-reference/endpoints/token-auth/attach", - "api-reference/endpoints/token-auth/retrieve", - "api-reference/endpoints/token-auth/update", - "api-reference/endpoints/token-auth/revoke", - "api-reference/endpoints/token-auth/get-tokens", - "api-reference/endpoints/token-auth/create-token", - "api-reference/endpoints/token-auth/update-token", - "api-reference/endpoints/token-auth/revoke-token" - ] - }, - { - "group": "Universal Auth", - "pages": [ - "api-reference/endpoints/universal-auth/login", - "api-reference/endpoints/universal-auth/attach", - "api-reference/endpoints/universal-auth/retrieve", - "api-reference/endpoints/universal-auth/update", - "api-reference/endpoints/universal-auth/revoke", - "api-reference/endpoints/universal-auth/create-client-secret", - "api-reference/endpoints/universal-auth/list-client-secrets", - "api-reference/endpoints/universal-auth/revoke-client-secret", - "api-reference/endpoints/universal-auth/get-client-secret-by-id", - "api-reference/endpoints/universal-auth/renew-access-token", - "api-reference/endpoints/universal-auth/revoke-access-token" - ] - }, - { - "group": "GCP Auth", - "pages": [ - "api-reference/endpoints/gcp-auth/login", - "api-reference/endpoints/gcp-auth/attach", - "api-reference/endpoints/gcp-auth/retrieve", - "api-reference/endpoints/gcp-auth/update", - "api-reference/endpoints/gcp-auth/revoke" - ] - }, - { - "group": "Alibaba Cloud Auth", - "pages": [ - "api-reference/endpoints/alicloud-auth/login", - "api-reference/endpoints/alicloud-auth/attach", - "api-reference/endpoints/alicloud-auth/retrieve", - "api-reference/endpoints/alicloud-auth/update", - "api-reference/endpoints/alicloud-auth/revoke" - ] - }, - { - "group": "AWS Auth", - "pages": [ - "api-reference/endpoints/aws-auth/login", - "api-reference/endpoints/aws-auth/attach", - "api-reference/endpoints/aws-auth/retrieve", - "api-reference/endpoints/aws-auth/update", - "api-reference/endpoints/aws-auth/revoke" - ] - }, - { - "group": "OCI Auth", - "pages": [ - "api-reference/endpoints/oci-auth/login", - "api-reference/endpoints/oci-auth/attach", - "api-reference/endpoints/oci-auth/retrieve", - "api-reference/endpoints/oci-auth/update", - "api-reference/endpoints/oci-auth/revoke" - ] - }, - { - "group": "Azure Auth", - "pages": [ - "api-reference/endpoints/azure-auth/login", - "api-reference/endpoints/azure-auth/attach", - "api-reference/endpoints/azure-auth/retrieve", - "api-reference/endpoints/azure-auth/update", - "api-reference/endpoints/azure-auth/revoke" - ] - }, - { - "group": "Kubernetes Auth", - "pages": [ - "api-reference/endpoints/kubernetes-auth/login", - "api-reference/endpoints/kubernetes-auth/attach", - "api-reference/endpoints/kubernetes-auth/retrieve", - "api-reference/endpoints/kubernetes-auth/update", - "api-reference/endpoints/kubernetes-auth/revoke" - ] - }, - { - "group": "OIDC Auth", - "pages": [ - "api-reference/endpoints/oidc-auth/login", - "api-reference/endpoints/oidc-auth/attach", - "api-reference/endpoints/oidc-auth/retrieve", - "api-reference/endpoints/oidc-auth/update", - "api-reference/endpoints/oidc-auth/revoke" - ] - }, - { - "group": "JWT Auth", - "pages": [ - "api-reference/endpoints/jwt-auth/login", - "api-reference/endpoints/jwt-auth/attach", - "api-reference/endpoints/jwt-auth/retrieve", - "api-reference/endpoints/jwt-auth/update", - "api-reference/endpoints/jwt-auth/revoke" - ] - }, - { - "group": "LDAP Auth", - "pages": [ - "api-reference/endpoints/ldap-auth/login", - "api-reference/endpoints/ldap-auth/attach", - "api-reference/endpoints/ldap-auth/retrieve", - "api-reference/endpoints/ldap-auth/update", - "api-reference/endpoints/ldap-auth/revoke" - ] - }, - { - "group": "Groups", - "pages": [ - "api-reference/endpoints/groups/create", - "api-reference/endpoints/groups/update", - "api-reference/endpoints/groups/delete", - "api-reference/endpoints/groups/get", - "api-reference/endpoints/groups/get-by-id", - "api-reference/endpoints/groups/add-group-user", - "api-reference/endpoints/groups/remove-group-user", - "api-reference/endpoints/groups/list-group-users" - ] - }, - { - "group": "Organizations", - "pages": [ - "api-reference/endpoints/organizations/memberships", - "api-reference/endpoints/organizations/update-membership", - "api-reference/endpoints/organizations/delete-membership", - "api-reference/endpoints/organizations/list-identity-memberships", - "api-reference/endpoints/organizations/workspaces" - ] - }, - { - "group": "Projects", - "pages": [ - "api-reference/endpoints/workspaces/create-workspace", - "api-reference/endpoints/workspaces/delete-workspace", - "api-reference/endpoints/workspaces/get-workspace", - "api-reference/endpoints/workspaces/update-workspace", - "api-reference/endpoints/workspaces/secret-snapshots" - ] - }, - { - "group": "Project Users", - "pages": [ - "api-reference/endpoints/project-users/invite-member-to-workspace", - "api-reference/endpoints/project-users/remove-member-from-workspace", - "api-reference/endpoints/project-users/memberships", - "api-reference/endpoints/project-users/get-by-username", - "api-reference/endpoints/project-users/update-membership" - ] - }, - { - "group": "Project Groups", - "pages": [ - "api-reference/endpoints/project-groups/create", - "api-reference/endpoints/project-groups/delete", - "api-reference/endpoints/project-groups/get-by-id", - "api-reference/endpoints/project-groups/list", - "api-reference/endpoints/project-groups/update" - ] - }, - { - "group": "Project Identities", - "pages": [ - "api-reference/endpoints/project-identities/add-identity-membership", - "api-reference/endpoints/project-identities/list-identity-memberships", - "api-reference/endpoints/project-identities/get-by-id", - "api-reference/endpoints/project-identities/update-identity-membership", - "api-reference/endpoints/project-identities/delete-identity-membership" - ] - }, - { - "group": "Project Roles", - "pages": [ - "api-reference/endpoints/project-roles/create", - "api-reference/endpoints/project-roles/update", - "api-reference/endpoints/project-roles/delete", - "api-reference/endpoints/project-roles/get-by-slug", - "api-reference/endpoints/project-roles/list" - ] - }, - { - "group": "Project Templates", - "pages": [ - "api-reference/endpoints/project-templates/create", - "api-reference/endpoints/project-templates/update", - "api-reference/endpoints/project-templates/delete", - "api-reference/endpoints/project-templates/get-by-id", - "api-reference/endpoints/project-templates/list" - ] - }, - { - "group": "Environments", - "pages": [ - "api-reference/endpoints/environments/create", - "api-reference/endpoints/environments/update", - "api-reference/endpoints/environments/delete" - ] - }, - { - "group": "Folders", - "pages": [ - "api-reference/endpoints/folders/list", - "api-reference/endpoints/folders/get-by-id", - "api-reference/endpoints/folders/create", - "api-reference/endpoints/folders/update", - "api-reference/endpoints/folders/delete" - ] - }, - { - "group": "Secret Tags", - "pages": [ - "api-reference/endpoints/secret-tags/list", - "api-reference/endpoints/secret-tags/get-by-id", - "api-reference/endpoints/secret-tags/get-by-slug", - "api-reference/endpoints/secret-tags/create", - "api-reference/endpoints/secret-tags/update", - "api-reference/endpoints/secret-tags/delete" - ] - }, - { - "group": "Secrets", - "pages": [ - "api-reference/endpoints/secrets/list", - "api-reference/endpoints/secrets/create", - "api-reference/endpoints/secrets/read", - "api-reference/endpoints/secrets/update", - "api-reference/endpoints/secrets/delete", - "api-reference/endpoints/secrets/create-many", - "api-reference/endpoints/secrets/update-many", - "api-reference/endpoints/secrets/delete-many", - "api-reference/endpoints/secrets/attach-tags", - "api-reference/endpoints/secrets/detach-tags" - ] - }, - { - "group": "Dynamic Secrets", - "pages": [ - { - "group": "Kubernetes", - "pages": [ - "api-reference/endpoints/dynamic-secrets/kubernetes/create-lease" - ] - }, - "api-reference/endpoints/dynamic-secrets/create", - "api-reference/endpoints/dynamic-secrets/update", - "api-reference/endpoints/dynamic-secrets/delete", - "api-reference/endpoints/dynamic-secrets/get", - "api-reference/endpoints/dynamic-secrets/list", - "api-reference/endpoints/dynamic-secrets/list-leases", - "api-reference/endpoints/dynamic-secrets/create-lease", - "api-reference/endpoints/dynamic-secrets/delete-lease", - "api-reference/endpoints/dynamic-secrets/renew-lease", - "api-reference/endpoints/dynamic-secrets/get-lease" - ] - }, - { - "group": "Secret Imports", - "pages": [ - "api-reference/endpoints/secret-imports/list", - "api-reference/endpoints/secret-imports/create", - "api-reference/endpoints/secret-imports/update", - "api-reference/endpoints/secret-imports/delete" - ] - }, - { - "group": "Secret Rotations", - "pages": [ - "api-reference/endpoints/secret-rotations/list", - "api-reference/endpoints/secret-rotations/options", - { - "group": "Auth0 Client Secret", - "pages": [ - "api-reference/endpoints/secret-rotations/auth0-client-secret/create", - "api-reference/endpoints/secret-rotations/auth0-client-secret/delete", - "api-reference/endpoints/secret-rotations/auth0-client-secret/get-by-id", - "api-reference/endpoints/secret-rotations/auth0-client-secret/get-by-name", - "api-reference/endpoints/secret-rotations/auth0-client-secret/get-generated-credentials-by-id", - "api-reference/endpoints/secret-rotations/auth0-client-secret/list", - "api-reference/endpoints/secret-rotations/auth0-client-secret/rotate-secrets", - "api-reference/endpoints/secret-rotations/auth0-client-secret/update" - ] - }, - { - "group": "AWS IAM User Secret", - "pages": [ - "api-reference/endpoints/secret-rotations/aws-iam-user-secret/create", - "api-reference/endpoints/secret-rotations/aws-iam-user-secret/delete", - "api-reference/endpoints/secret-rotations/aws-iam-user-secret/get-by-id", - "api-reference/endpoints/secret-rotations/aws-iam-user-secret/get-by-name", - "api-reference/endpoints/secret-rotations/aws-iam-user-secret/get-generated-credentials-by-id", - "api-reference/endpoints/secret-rotations/aws-iam-user-secret/list", - "api-reference/endpoints/secret-rotations/aws-iam-user-secret/rotate-secrets", - "api-reference/endpoints/secret-rotations/aws-iam-user-secret/update" - ] - }, - { - "group": "Azure Client Secret", - "pages": [ - "api-reference/endpoints/secret-rotations/azure-client-secret/create", - "api-reference/endpoints/secret-rotations/azure-client-secret/delete", - "api-reference/endpoints/secret-rotations/azure-client-secret/get-by-id", - "api-reference/endpoints/secret-rotations/azure-client-secret/get-by-name", - "api-reference/endpoints/secret-rotations/azure-client-secret/get-generated-credentials-by-id", - "api-reference/endpoints/secret-rotations/azure-client-secret/list", - "api-reference/endpoints/secret-rotations/azure-client-secret/rotate-secrets", - "api-reference/endpoints/secret-rotations/azure-client-secret/update" - ] - }, - { - "group": "LDAP Password", - "pages": [ - "api-reference/endpoints/secret-rotations/ldap-password/create", - "api-reference/endpoints/secret-rotations/ldap-password/delete", - "api-reference/endpoints/secret-rotations/ldap-password/get-by-id", - "api-reference/endpoints/secret-rotations/ldap-password/get-by-name", - "api-reference/endpoints/secret-rotations/ldap-password/get-generated-credentials-by-id", - "api-reference/endpoints/secret-rotations/ldap-password/list", - "api-reference/endpoints/secret-rotations/ldap-password/rotate-secrets", - "api-reference/endpoints/secret-rotations/ldap-password/update" - ] - }, - { - "group": "Microsoft SQL Server Credentials", - "pages": [ - "api-reference/endpoints/secret-rotations/mssql-credentials/create", - "api-reference/endpoints/secret-rotations/mssql-credentials/delete", - "api-reference/endpoints/secret-rotations/mssql-credentials/get-by-id", - "api-reference/endpoints/secret-rotations/mssql-credentials/get-by-name", - "api-reference/endpoints/secret-rotations/mssql-credentials/get-generated-credentials-by-id", - "api-reference/endpoints/secret-rotations/mssql-credentials/list", - "api-reference/endpoints/secret-rotations/mssql-credentials/rotate-secrets", - "api-reference/endpoints/secret-rotations/mssql-credentials/update" - ] - }, - { - "group": "MySQL Credentials", - "pages": [ - "api-reference/endpoints/secret-rotations/mysql-credentials/create", - "api-reference/endpoints/secret-rotations/mysql-credentials/delete", - "api-reference/endpoints/secret-rotations/mysql-credentials/get-by-id", - "api-reference/endpoints/secret-rotations/mysql-credentials/get-by-name", - "api-reference/endpoints/secret-rotations/mysql-credentials/get-generated-credentials-by-id", - "api-reference/endpoints/secret-rotations/mysql-credentials/list", - "api-reference/endpoints/secret-rotations/mysql-credentials/rotate-secrets", - "api-reference/endpoints/secret-rotations/mysql-credentials/update" - ] - }, - { - "group": "OracleDB Credentials", - "pages": [ - "api-reference/endpoints/secret-rotations/oracledb-credentials/create", - "api-reference/endpoints/secret-rotations/oracledb-credentials/delete", - "api-reference/endpoints/secret-rotations/oracledb-credentials/get-by-id", - "api-reference/endpoints/secret-rotations/oracledb-credentials/get-by-name", - "api-reference/endpoints/secret-rotations/oracledb-credentials/get-generated-credentials-by-id", - "api-reference/endpoints/secret-rotations/oracledb-credentials/list", - "api-reference/endpoints/secret-rotations/oracledb-credentials/rotate-secrets", - "api-reference/endpoints/secret-rotations/oracledb-credentials/update" - ] - }, - { - "group": "PostgreSQL Credentials", - "pages": [ - "api-reference/endpoints/secret-rotations/postgres-credentials/create", - "api-reference/endpoints/secret-rotations/postgres-credentials/delete", - "api-reference/endpoints/secret-rotations/postgres-credentials/get-by-id", - "api-reference/endpoints/secret-rotations/postgres-credentials/get-by-name", - "api-reference/endpoints/secret-rotations/postgres-credentials/get-generated-credentials-by-id", - "api-reference/endpoints/secret-rotations/postgres-credentials/list", - "api-reference/endpoints/secret-rotations/postgres-credentials/rotate-secrets", - "api-reference/endpoints/secret-rotations/postgres-credentials/update" - ] - } - ] - }, - { - "group": "Secret Scanning", - "pages": [ - { - "group": "Data Sources", - "pages": [ - "api-reference/endpoints/secret-scanning/data-sources/list", - "api-reference/endpoints/secret-scanning/data-sources/options", - { - "group": "GitHub", - "pages": [ - "api-reference/endpoints/secret-scanning/data-sources/github/list", - "api-reference/endpoints/secret-scanning/data-sources/github/get-by-id", - "api-reference/endpoints/secret-scanning/data-sources/github/get-by-name", - "api-reference/endpoints/secret-scanning/data-sources/github/list-resources", - "api-reference/endpoints/secret-scanning/data-sources/github/list-scans", - "api-reference/endpoints/secret-scanning/data-sources/github/create", - "api-reference/endpoints/secret-scanning/data-sources/github/update", - "api-reference/endpoints/secret-scanning/data-sources/github/delete", - "api-reference/endpoints/secret-scanning/data-sources/github/scan", - "api-reference/endpoints/secret-scanning/data-sources/github/scan-resource" - ] - } - ] - }, - { - "group": "Findings", - "pages": [ - "api-reference/endpoints/secret-scanning/findings/list", - "api-reference/endpoints/secret-scanning/findings/update" - ] - }, - { - "group": "Configuration", - "pages": [ - "api-reference/endpoints/secret-scanning/config/get-by-project-id", - "api-reference/endpoints/secret-scanning/config/update" - ] - } - ] - }, - { - "group": "Identity Specific Privilege", - "pages": [ - { - "group": "V1 (Legacy)", - "pages": [ - "api-reference/endpoints/identity-specific-privilege/v1/create-permanent", - "api-reference/endpoints/identity-specific-privilege/v1/create-temporary", - "api-reference/endpoints/identity-specific-privilege/v1/update", - "api-reference/endpoints/identity-specific-privilege/v1/delete", - "api-reference/endpoints/identity-specific-privilege/v1/find-by-slug", - "api-reference/endpoints/identity-specific-privilege/v1/list" - ] - }, - { - "group": "V2", - "pages": [ - "api-reference/endpoints/identity-specific-privilege/v2/create", - "api-reference/endpoints/identity-specific-privilege/v2/update", - "api-reference/endpoints/identity-specific-privilege/v2/delete", - "api-reference/endpoints/identity-specific-privilege/v2/list", - "api-reference/endpoints/identity-specific-privilege/v2/find-by-id", - "api-reference/endpoints/identity-specific-privilege/v2/find-by-slug" - ] - } - ] - }, - { - "group": "App Connections", - "pages": [ - "api-reference/endpoints/app-connections/list", - "api-reference/endpoints/app-connections/options", - { - "group": "1Password", - "pages": [ - "api-reference/endpoints/app-connections/1password/list", - "api-reference/endpoints/app-connections/1password/available", - "api-reference/endpoints/app-connections/1password/get-by-id", - "api-reference/endpoints/app-connections/1password/get-by-name", - "api-reference/endpoints/app-connections/1password/create", - "api-reference/endpoints/app-connections/1password/update", - "api-reference/endpoints/app-connections/1password/delete" - ] - }, - { - "group": "Auth0", - "pages": [ - "api-reference/endpoints/app-connections/auth0/list", - "api-reference/endpoints/app-connections/auth0/available", - "api-reference/endpoints/app-connections/auth0/get-by-id", - "api-reference/endpoints/app-connections/auth0/get-by-name", - "api-reference/endpoints/app-connections/auth0/create", - "api-reference/endpoints/app-connections/auth0/update", - "api-reference/endpoints/app-connections/auth0/delete" - ] - }, - { - "group": "AWS", - "pages": [ - "api-reference/endpoints/app-connections/aws/list", - "api-reference/endpoints/app-connections/aws/available", - "api-reference/endpoints/app-connections/aws/get-by-id", - "api-reference/endpoints/app-connections/aws/get-by-name", - "api-reference/endpoints/app-connections/aws/create", - "api-reference/endpoints/app-connections/aws/update", - "api-reference/endpoints/app-connections/aws/delete" - ] - }, - { - "group": "Azure App Configuration", - "pages": [ - "api-reference/endpoints/app-connections/azure-app-configuration/list", - "api-reference/endpoints/app-connections/azure-app-configuration/available", - "api-reference/endpoints/app-connections/azure-app-configuration/get-by-id", - "api-reference/endpoints/app-connections/azure-app-configuration/get-by-name", - "api-reference/endpoints/app-connections/azure-app-configuration/create", - "api-reference/endpoints/app-connections/azure-app-configuration/update", - "api-reference/endpoints/app-connections/azure-app-configuration/delete" - ] - }, - { - "group": "Azure Client Secret", - "pages": [ - "api-reference/endpoints/app-connections/azure-client-secret/list", - "api-reference/endpoints/app-connections/azure-client-secret/available", - "api-reference/endpoints/app-connections/azure-client-secret/get-by-id", - "api-reference/endpoints/app-connections/azure-client-secret/get-by-name", - "api-reference/endpoints/app-connections/azure-client-secret/create", - "api-reference/endpoints/app-connections/azure-client-secret/update", - "api-reference/endpoints/app-connections/azure-client-secret/delete" - ] - }, - { - "group": "Azure DevOps", - "pages": [ - "api-reference/endpoints/app-connections/azure-devops/list", - "api-reference/endpoints/app-connections/azure-devops/available", - "api-reference/endpoints/app-connections/azure-devops/get-by-id", - "api-reference/endpoints/app-connections/azure-devops/get-by-name", - "api-reference/endpoints/app-connections/azure-devops/create", - "api-reference/endpoints/app-connections/azure-devops/update", - "api-reference/endpoints/app-connections/azure-devops/delete" - ] - }, - { - "group": "Azure Key Vault", - "pages": [ - "api-reference/endpoints/app-connections/azure-key-vault/list", - "api-reference/endpoints/app-connections/azure-key-vault/available", - "api-reference/endpoints/app-connections/azure-key-vault/get-by-id", - "api-reference/endpoints/app-connections/azure-key-vault/get-by-name", - "api-reference/endpoints/app-connections/azure-key-vault/create", - "api-reference/endpoints/app-connections/azure-key-vault/update", - "api-reference/endpoints/app-connections/azure-key-vault/delete" - ] - }, - { - "group": "Camunda", - "pages": [ - "api-reference/endpoints/app-connections/camunda/list", - "api-reference/endpoints/app-connections/camunda/available", - "api-reference/endpoints/app-connections/camunda/get-by-id", - "api-reference/endpoints/app-connections/camunda/get-by-name", - "api-reference/endpoints/app-connections/camunda/create", - "api-reference/endpoints/app-connections/camunda/update", - "api-reference/endpoints/app-connections/camunda/delete" - ] - }, - { - "group": "Databricks", - "pages": [ - "api-reference/endpoints/app-connections/databricks/list", - "api-reference/endpoints/app-connections/databricks/available", - "api-reference/endpoints/app-connections/databricks/get-by-id", - "api-reference/endpoints/app-connections/databricks/get-by-name", - "api-reference/endpoints/app-connections/databricks/create", - "api-reference/endpoints/app-connections/databricks/update", - "api-reference/endpoints/app-connections/databricks/delete" - ] - }, - { - "group": "Fly.io", - "pages": [ - "api-reference/endpoints/app-connections/flyio/list", - "api-reference/endpoints/app-connections/flyio/available", - "api-reference/endpoints/app-connections/flyio/get-by-id", - "api-reference/endpoints/app-connections/flyio/get-by-name", - "api-reference/endpoints/app-connections/flyio/create", - "api-reference/endpoints/app-connections/flyio/update", - "api-reference/endpoints/app-connections/flyio/delete" - ] - }, - { - "group": "GCP", - "pages": [ - "api-reference/endpoints/app-connections/gcp/list", - "api-reference/endpoints/app-connections/gcp/available", - "api-reference/endpoints/app-connections/gcp/get-by-id", - "api-reference/endpoints/app-connections/gcp/get-by-name", - "api-reference/endpoints/app-connections/gcp/create", - "api-reference/endpoints/app-connections/gcp/update", - "api-reference/endpoints/app-connections/gcp/delete" - ] - }, - { - "group": "GitHub", - "pages": [ - "api-reference/endpoints/app-connections/github/list", - "api-reference/endpoints/app-connections/github/available", - "api-reference/endpoints/app-connections/github/get-by-id", - "api-reference/endpoints/app-connections/github/get-by-name", - "api-reference/endpoints/app-connections/github/create", - "api-reference/endpoints/app-connections/github/update", - "api-reference/endpoints/app-connections/github/delete" - ] - }, - { - "group": "GitLab", - "pages": [ - "api-reference/endpoints/app-connections/gitlab/list", - "api-reference/endpoints/app-connections/gitlab/available", - "api-reference/endpoints/app-connections/gitlab/get-by-id", - "api-reference/endpoints/app-connections/gitlab/get-by-name", - "api-reference/endpoints/app-connections/gitlab/create", - "api-reference/endpoints/app-connections/gitlab/update", - "api-reference/endpoints/app-connections/gitlab/delete" - ] - }, - { - "group": "GitHub Radar", - "pages": [ - "api-reference/endpoints/app-connections/github-radar/list", - "api-reference/endpoints/app-connections/github-radar/available", - "api-reference/endpoints/app-connections/github-radar/get-by-id", - "api-reference/endpoints/app-connections/github-radar/get-by-name", - "api-reference/endpoints/app-connections/github-radar/create", - "api-reference/endpoints/app-connections/github-radar/update", - "api-reference/endpoints/app-connections/github-radar/delete" - ] - }, - { - "group": "Hashicorp Vault", - "pages": [ - "api-reference/endpoints/app-connections/hashicorp-vault/list", - "api-reference/endpoints/app-connections/hashicorp-vault/available", - "api-reference/endpoints/app-connections/hashicorp-vault/get-by-id", - "api-reference/endpoints/app-connections/hashicorp-vault/get-by-name", - "api-reference/endpoints/app-connections/hashicorp-vault/create", - "api-reference/endpoints/app-connections/hashicorp-vault/update", - "api-reference/endpoints/app-connections/hashicorp-vault/delete" - ] - }, - { - "group": "Heroku", - "pages": [ - "api-reference/endpoints/app-connections/heroku/list", - "api-reference/endpoints/app-connections/heroku/available", - "api-reference/endpoints/app-connections/heroku/get-by-id", - "api-reference/endpoints/app-connections/heroku/get-by-name", - "api-reference/endpoints/app-connections/heroku/create", - "api-reference/endpoints/app-connections/heroku/update", - "api-reference/endpoints/app-connections/heroku/delete" - ] - }, - { - "group": "Humanitec", - "pages": [ - "api-reference/endpoints/app-connections/humanitec/list", - "api-reference/endpoints/app-connections/humanitec/available", - "api-reference/endpoints/app-connections/humanitec/get-by-id", - "api-reference/endpoints/app-connections/humanitec/get-by-name", - "api-reference/endpoints/app-connections/humanitec/create", - "api-reference/endpoints/app-connections/humanitec/update", - "api-reference/endpoints/app-connections/humanitec/delete" - ] - }, - { - "group": "LDAP", - "pages": [ - "api-reference/endpoints/app-connections/ldap/list", - "api-reference/endpoints/app-connections/ldap/available", - "api-reference/endpoints/app-connections/ldap/get-by-id", - "api-reference/endpoints/app-connections/ldap/get-by-name", - "api-reference/endpoints/app-connections/ldap/create", - "api-reference/endpoints/app-connections/ldap/update", - "api-reference/endpoints/app-connections/ldap/delete" - ] - }, - { - "group": "Microsoft SQL Server", - "pages": [ - "api-reference/endpoints/app-connections/mssql/list", - "api-reference/endpoints/app-connections/mssql/available", - "api-reference/endpoints/app-connections/mssql/get-by-id", - "api-reference/endpoints/app-connections/mssql/get-by-name", - "api-reference/endpoints/app-connections/mssql/create", - "api-reference/endpoints/app-connections/mssql/update", - "api-reference/endpoints/app-connections/mssql/delete" - ] - }, - { - "group": "MySQL", - "pages": [ - "api-reference/endpoints/app-connections/mysql/list", - "api-reference/endpoints/app-connections/mysql/available", - "api-reference/endpoints/app-connections/mysql/get-by-id", - "api-reference/endpoints/app-connections/mysql/get-by-name", - "api-reference/endpoints/app-connections/mysql/create", - "api-reference/endpoints/app-connections/mysql/update", - "api-reference/endpoints/app-connections/mysql/delete" - ] - }, - { - "group": "OCI", - "pages": [ - "api-reference/endpoints/app-connections/oci/list", - "api-reference/endpoints/app-connections/oci/available", - "api-reference/endpoints/app-connections/oci/get-by-id", - "api-reference/endpoints/app-connections/oci/get-by-name", - "api-reference/endpoints/app-connections/oci/create", - "api-reference/endpoints/app-connections/oci/update", - "api-reference/endpoints/app-connections/oci/delete" - ] - }, - { - "group": "OracleDB", - "pages": [ - "api-reference/endpoints/app-connections/oracledb/list", - "api-reference/endpoints/app-connections/oracledb/available", - "api-reference/endpoints/app-connections/oracledb/get-by-id", - "api-reference/endpoints/app-connections/oracledb/get-by-name", - "api-reference/endpoints/app-connections/oracledb/create", - "api-reference/endpoints/app-connections/oracledb/update", - "api-reference/endpoints/app-connections/oracledb/delete" - ] - }, - { - "group": "PostgreSQL", - "pages": [ - "api-reference/endpoints/app-connections/postgres/list", - "api-reference/endpoints/app-connections/postgres/available", - "api-reference/endpoints/app-connections/postgres/get-by-id", - "api-reference/endpoints/app-connections/postgres/get-by-name", - "api-reference/endpoints/app-connections/postgres/create", - "api-reference/endpoints/app-connections/postgres/update", - "api-reference/endpoints/app-connections/postgres/delete" - ] - }, - { - "group": "Render", - "pages": [ - "api-reference/endpoints/app-connections/render/list", - "api-reference/endpoints/app-connections/render/available", - "api-reference/endpoints/app-connections/render/get-by-id", - "api-reference/endpoints/app-connections/render/get-by-name", - "api-reference/endpoints/app-connections/render/create", - "api-reference/endpoints/app-connections/render/update", - "api-reference/endpoints/app-connections/render/delete" - ] - }, - { - "group": "TeamCity", - "pages": [ - "api-reference/endpoints/app-connections/teamcity/list", - "api-reference/endpoints/app-connections/teamcity/available", - "api-reference/endpoints/app-connections/teamcity/get-by-id", - "api-reference/endpoints/app-connections/teamcity/get-by-name", - "api-reference/endpoints/app-connections/teamcity/create", - "api-reference/endpoints/app-connections/teamcity/update", - "api-reference/endpoints/app-connections/teamcity/delete" - ] - }, - { - "group": "Terraform Cloud", - "pages": [ - "api-reference/endpoints/app-connections/terraform-cloud/list", - "api-reference/endpoints/app-connections/terraform-cloud/available", - "api-reference/endpoints/app-connections/terraform-cloud/get-by-id", - "api-reference/endpoints/app-connections/terraform-cloud/get-by-name", - "api-reference/endpoints/app-connections/terraform-cloud/create", - "api-reference/endpoints/app-connections/terraform-cloud/update", - "api-reference/endpoints/app-connections/terraform-cloud/delete" - ] - }, - { - "group": "Vercel", - "pages": [ - "api-reference/endpoints/app-connections/vercel/list", - "api-reference/endpoints/app-connections/vercel/available", - "api-reference/endpoints/app-connections/vercel/get-by-id", - "api-reference/endpoints/app-connections/vercel/get-by-name", - "api-reference/endpoints/app-connections/vercel/create", - "api-reference/endpoints/app-connections/vercel/update", - "api-reference/endpoints/app-connections/vercel/delete" - ] - }, - { - "group": "Windmill", - "pages": [ - "api-reference/endpoints/app-connections/windmill/list", - "api-reference/endpoints/app-connections/windmill/available", - "api-reference/endpoints/app-connections/windmill/get-by-id", - "api-reference/endpoints/app-connections/windmill/get-by-name", - "api-reference/endpoints/app-connections/windmill/create", - "api-reference/endpoints/app-connections/windmill/update", - "api-reference/endpoints/app-connections/windmill/delete" - ] - } - ] - }, - { - "group": "Secret Syncs", - "pages": [ - "api-reference/endpoints/secret-syncs/list", - "api-reference/endpoints/secret-syncs/options", - { - "group": "1Password", - "pages": [ - "api-reference/endpoints/secret-syncs/1password/list", - "api-reference/endpoints/secret-syncs/1password/get-by-id", - "api-reference/endpoints/secret-syncs/1password/get-by-name", - "api-reference/endpoints/secret-syncs/1password/create", - "api-reference/endpoints/secret-syncs/1password/update", - "api-reference/endpoints/secret-syncs/1password/delete", - "api-reference/endpoints/secret-syncs/1password/sync-secrets", - "api-reference/endpoints/secret-syncs/1password/import-secrets", - "api-reference/endpoints/secret-syncs/1password/remove-secrets" - ] - }, - { - "group": "AWS Parameter Store", - "pages": [ - "api-reference/endpoints/secret-syncs/aws-parameter-store/list", - "api-reference/endpoints/secret-syncs/aws-parameter-store/get-by-id", - "api-reference/endpoints/secret-syncs/aws-parameter-store/get-by-name", - "api-reference/endpoints/secret-syncs/aws-parameter-store/create", - "api-reference/endpoints/secret-syncs/aws-parameter-store/update", - "api-reference/endpoints/secret-syncs/aws-parameter-store/delete", - "api-reference/endpoints/secret-syncs/aws-parameter-store/sync-secrets", - "api-reference/endpoints/secret-syncs/aws-parameter-store/import-secrets", - "api-reference/endpoints/secret-syncs/aws-parameter-store/remove-secrets" - ] - }, - { - "group": "AWS Secrets Manager", - "pages": [ - "api-reference/endpoints/secret-syncs/aws-secrets-manager/list", - "api-reference/endpoints/secret-syncs/aws-secrets-manager/get-by-id", - "api-reference/endpoints/secret-syncs/aws-secrets-manager/get-by-name", - "api-reference/endpoints/secret-syncs/aws-secrets-manager/create", - "api-reference/endpoints/secret-syncs/aws-secrets-manager/update", - "api-reference/endpoints/secret-syncs/aws-secrets-manager/delete", - "api-reference/endpoints/secret-syncs/aws-secrets-manager/sync-secrets", - "api-reference/endpoints/secret-syncs/aws-secrets-manager/import-secrets", - "api-reference/endpoints/secret-syncs/aws-secrets-manager/remove-secrets" - ] - }, - { - "group": "Azure App Configuration", - "pages": [ - "api-reference/endpoints/secret-syncs/azure-app-configuration/list", - "api-reference/endpoints/secret-syncs/azure-app-configuration/get-by-id", - "api-reference/endpoints/secret-syncs/azure-app-configuration/get-by-name", - "api-reference/endpoints/secret-syncs/azure-app-configuration/create", - "api-reference/endpoints/secret-syncs/azure-app-configuration/update", - "api-reference/endpoints/secret-syncs/azure-app-configuration/delete", - "api-reference/endpoints/secret-syncs/azure-app-configuration/sync-secrets", - "api-reference/endpoints/secret-syncs/azure-app-configuration/import-secrets", - "api-reference/endpoints/secret-syncs/azure-app-configuration/remove-secrets" - ] - }, - { - "group": "Azure DevOps", - "pages": [ - "api-reference/endpoints/secret-syncs/azure-devops/list", - "api-reference/endpoints/secret-syncs/azure-devops/get-by-id", - "api-reference/endpoints/secret-syncs/azure-devops/get-by-name", - "api-reference/endpoints/secret-syncs/azure-devops/create", - "api-reference/endpoints/secret-syncs/azure-devops/update", - "api-reference/endpoints/secret-syncs/azure-devops/delete", - "api-reference/endpoints/secret-syncs/azure-devops/sync-secrets", - "api-reference/endpoints/secret-syncs/azure-devops/import-secrets", - "api-reference/endpoints/secret-syncs/azure-devops/remove-secrets" - ] - }, - { - "group": "Azure Key Vault", - "pages": [ - "api-reference/endpoints/secret-syncs/azure-key-vault/list", - "api-reference/endpoints/secret-syncs/azure-key-vault/get-by-id", - "api-reference/endpoints/secret-syncs/azure-key-vault/get-by-name", - "api-reference/endpoints/secret-syncs/azure-key-vault/create", - "api-reference/endpoints/secret-syncs/azure-key-vault/update", - "api-reference/endpoints/secret-syncs/azure-key-vault/delete", - "api-reference/endpoints/secret-syncs/azure-key-vault/sync-secrets", - "api-reference/endpoints/secret-syncs/azure-key-vault/import-secrets", - "api-reference/endpoints/secret-syncs/azure-key-vault/remove-secrets" - ] - }, - { - "group": "Camunda", - "pages": [ - "api-reference/endpoints/secret-syncs/camunda/list", - "api-reference/endpoints/secret-syncs/camunda/get-by-id", - "api-reference/endpoints/secret-syncs/camunda/get-by-name", - "api-reference/endpoints/secret-syncs/camunda/create", - "api-reference/endpoints/secret-syncs/camunda/update", - "api-reference/endpoints/secret-syncs/camunda/delete", - "api-reference/endpoints/secret-syncs/camunda/sync-secrets", - "api-reference/endpoints/secret-syncs/camunda/remove-secrets" - ] - }, - { - "group": "Databricks", - "pages": [ - "api-reference/endpoints/secret-syncs/databricks/list", - "api-reference/endpoints/secret-syncs/databricks/get-by-id", - "api-reference/endpoints/secret-syncs/databricks/get-by-name", - "api-reference/endpoints/secret-syncs/databricks/create", - "api-reference/endpoints/secret-syncs/databricks/update", - "api-reference/endpoints/secret-syncs/databricks/delete", - "api-reference/endpoints/secret-syncs/databricks/sync-secrets", - "api-reference/endpoints/secret-syncs/databricks/remove-secrets" - ] - }, - { - "group": "Fly.io", - "pages": [ - "api-reference/endpoints/secret-syncs/flyio/list", - "api-reference/endpoints/secret-syncs/flyio/get-by-id", - "api-reference/endpoints/secret-syncs/flyio/get-by-name", - "api-reference/endpoints/secret-syncs/flyio/create", - "api-reference/endpoints/secret-syncs/flyio/update", - "api-reference/endpoints/secret-syncs/flyio/delete", - "api-reference/endpoints/secret-syncs/flyio/sync-secrets", - "api-reference/endpoints/secret-syncs/flyio/remove-secrets" - ] - }, - { - "group": "GCP Secret Manager", - "pages": [ - "api-reference/endpoints/secret-syncs/gcp-secret-manager/list", - "api-reference/endpoints/secret-syncs/gcp-secret-manager/get-by-id", - "api-reference/endpoints/secret-syncs/gcp-secret-manager/get-by-name", - "api-reference/endpoints/secret-syncs/gcp-secret-manager/create", - "api-reference/endpoints/secret-syncs/gcp-secret-manager/update", - "api-reference/endpoints/secret-syncs/gcp-secret-manager/delete", - "api-reference/endpoints/secret-syncs/gcp-secret-manager/sync-secrets", - "api-reference/endpoints/secret-syncs/gcp-secret-manager/import-secrets", - "api-reference/endpoints/secret-syncs/gcp-secret-manager/remove-secrets" - ] - }, - { - "group": "GitHub", - "pages": [ - "api-reference/endpoints/secret-syncs/github/list", - "api-reference/endpoints/secret-syncs/github/get-by-id", - "api-reference/endpoints/secret-syncs/github/get-by-name", - "api-reference/endpoints/secret-syncs/github/create", - "api-reference/endpoints/secret-syncs/github/update", - "api-reference/endpoints/secret-syncs/github/delete", - "api-reference/endpoints/secret-syncs/github/sync-secrets", - "api-reference/endpoints/secret-syncs/github/remove-secrets" - ] - }, - { - "group": "GitLab", - "pages": [ - "api-reference/endpoints/secret-syncs/gitlab/list", - "api-reference/endpoints/secret-syncs/gitlab/get-by-id", - "api-reference/endpoints/secret-syncs/gitlab/get-by-name", - "api-reference/endpoints/secret-syncs/gitlab/create", - "api-reference/endpoints/secret-syncs/gitlab/update", - "api-reference/endpoints/secret-syncs/gitlab/delete", - "api-reference/endpoints/secret-syncs/gitlab/sync-secrets", - "api-reference/endpoints/secret-syncs/gitlab/remove-secrets" - ] - }, - { - "group": "Hashicorp Vault", - "pages": [ - "api-reference/endpoints/secret-syncs/hashicorp-vault/list", - "api-reference/endpoints/secret-syncs/hashicorp-vault/get-by-id", - "api-reference/endpoints/secret-syncs/hashicorp-vault/get-by-name", - "api-reference/endpoints/secret-syncs/hashicorp-vault/create", - "api-reference/endpoints/secret-syncs/hashicorp-vault/update", - "api-reference/endpoints/secret-syncs/hashicorp-vault/delete", - "api-reference/endpoints/secret-syncs/hashicorp-vault/sync-secrets", - "api-reference/endpoints/secret-syncs/hashicorp-vault/import-secrets", - "api-reference/endpoints/secret-syncs/hashicorp-vault/remove-secrets" - ] - }, - { - "group": "Heroku", - "pages": [ - "api-reference/endpoints/secret-syncs/heroku/list", - "api-reference/endpoints/secret-syncs/heroku/get-by-id", - "api-reference/endpoints/secret-syncs/heroku/get-by-name", - "api-reference/endpoints/secret-syncs/heroku/create", - "api-reference/endpoints/secret-syncs/heroku/update", - "api-reference/endpoints/secret-syncs/heroku/delete", - "api-reference/endpoints/secret-syncs/heroku/sync-secrets", - "api-reference/endpoints/secret-syncs/heroku/remove-secrets" - ] - }, - { - "group": "Humanitec", - "pages": [ - "api-reference/endpoints/secret-syncs/humanitec/list", - "api-reference/endpoints/secret-syncs/humanitec/get-by-id", - "api-reference/endpoints/secret-syncs/humanitec/get-by-name", - "api-reference/endpoints/secret-syncs/humanitec/create", - "api-reference/endpoints/secret-syncs/humanitec/update", - "api-reference/endpoints/secret-syncs/humanitec/delete", - "api-reference/endpoints/secret-syncs/humanitec/sync-secrets", - "api-reference/endpoints/secret-syncs/humanitec/remove-secrets" - ] - }, - { - "group": "OCI", - "pages": [ - "api-reference/endpoints/secret-syncs/oci-vault/list", - "api-reference/endpoints/secret-syncs/oci-vault/get-by-id", - "api-reference/endpoints/secret-syncs/oci-vault/get-by-name", - "api-reference/endpoints/secret-syncs/oci-vault/create", - "api-reference/endpoints/secret-syncs/oci-vault/update", - "api-reference/endpoints/secret-syncs/oci-vault/delete", - "api-reference/endpoints/secret-syncs/oci-vault/sync-secrets", - "api-reference/endpoints/secret-syncs/oci-vault/import-secrets", - "api-reference/endpoints/secret-syncs/oci-vault/remove-secrets" - ] - }, - { - "group": "Render", - "pages": [ - "api-reference/endpoints/secret-syncs/render/list", - "api-reference/endpoints/secret-syncs/render/get-by-id", - "api-reference/endpoints/secret-syncs/render/get-by-name", - "api-reference/endpoints/secret-syncs/render/create", - "api-reference/endpoints/secret-syncs/render/update", - "api-reference/endpoints/secret-syncs/render/delete", - "api-reference/endpoints/secret-syncs/render/sync-secrets", - "api-reference/endpoints/secret-syncs/render/import-secrets", - "api-reference/endpoints/secret-syncs/render/remove-secrets" - ] - }, - { - "group": "TeamCity", - "pages": [ - "api-reference/endpoints/secret-syncs/teamcity/list", - "api-reference/endpoints/secret-syncs/teamcity/get-by-id", - "api-reference/endpoints/secret-syncs/teamcity/get-by-name", - "api-reference/endpoints/secret-syncs/teamcity/create", - "api-reference/endpoints/secret-syncs/teamcity/update", - "api-reference/endpoints/secret-syncs/teamcity/delete", - "api-reference/endpoints/secret-syncs/teamcity/sync-secrets", - "api-reference/endpoints/secret-syncs/teamcity/import-secrets", - "api-reference/endpoints/secret-syncs/teamcity/remove-secrets" - ] - }, - { - "group": "Terraform Cloud", - "pages": [ - "api-reference/endpoints/secret-syncs/terraform-cloud/list", - "api-reference/endpoints/secret-syncs/terraform-cloud/get-by-id", - "api-reference/endpoints/secret-syncs/terraform-cloud/get-by-name", - "api-reference/endpoints/secret-syncs/terraform-cloud/create", - "api-reference/endpoints/secret-syncs/terraform-cloud/update", - "api-reference/endpoints/secret-syncs/terraform-cloud/delete", - "api-reference/endpoints/secret-syncs/terraform-cloud/sync-secrets", - "api-reference/endpoints/secret-syncs/terraform-cloud/remove-secrets" - ] - }, - { - "group": "Vercel", - "pages": [ - "api-reference/endpoints/secret-syncs/vercel/list", - "api-reference/endpoints/secret-syncs/vercel/get-by-id", - "api-reference/endpoints/secret-syncs/vercel/get-by-name", - "api-reference/endpoints/secret-syncs/vercel/create", - "api-reference/endpoints/secret-syncs/vercel/update", - "api-reference/endpoints/secret-syncs/vercel/delete", - "api-reference/endpoints/secret-syncs/vercel/sync-secrets", - "api-reference/endpoints/secret-syncs/vercel/import-secrets", - "api-reference/endpoints/secret-syncs/vercel/remove-secrets" - ] - }, - { - "group": "Windmill", - "pages": [ - "api-reference/endpoints/secret-syncs/windmill/list", - "api-reference/endpoints/secret-syncs/windmill/get-by-id", - "api-reference/endpoints/secret-syncs/windmill/get-by-name", - "api-reference/endpoints/secret-syncs/windmill/create", - "api-reference/endpoints/secret-syncs/windmill/update", - "api-reference/endpoints/secret-syncs/windmill/delete", - "api-reference/endpoints/secret-syncs/windmill/sync-secrets", - "api-reference/endpoints/secret-syncs/windmill/import-secrets", - "api-reference/endpoints/secret-syncs/windmill/remove-secrets" - ] - } - ] - }, - { - "group": "Integrations", - "pages": [ - "api-reference/endpoints/integrations/create-auth", - "api-reference/endpoints/integrations/list-auth", - "api-reference/endpoints/integrations/find-auth", - "api-reference/endpoints/integrations/delete-auth", - "api-reference/endpoints/integrations/delete-auth-by-id", - "api-reference/endpoints/integrations/create", - "api-reference/endpoints/integrations/update", - "api-reference/endpoints/integrations/delete", - "api-reference/endpoints/integrations/list-project-integrations" - ] - }, - { - "group": "Service Tokens", - "pages": ["api-reference/endpoints/service-tokens/get"] - }, - { - "group": "Audit Logs", - "pages": ["api-reference/endpoints/audit-logs/export-audit-log"] - } - ] - }, - { - "group": "Infisical PKI", - "pages": [ - { - "group": "Subscribers", - "pages": [ - "api-reference/endpoints/pki/subscribers/list-certs", - "api-reference/endpoints/pki/subscribers/create", - "api-reference/endpoints/pki/subscribers/read", - "api-reference/endpoints/pki/subscribers/update", - "api-reference/endpoints/pki/subscribers/delete", - "api-reference/endpoints/pki/subscribers/issue-cert", - "api-reference/endpoints/pki/subscribers/sign-cert", - "api-reference/endpoints/pki/subscribers/order-cert", - "api-reference/endpoints/pki/subscribers/get-latest-cert-bundle" - ] - }, - { - "group": "Certificate Authorities", - "pages": [ - { - "group": "ACME", - "pages": [ - "api-reference/endpoints/certificate-authorities/acme/list", - "api-reference/endpoints/certificate-authorities/acme/create", - "api-reference/endpoints/certificate-authorities/acme/read", - "api-reference/endpoints/certificate-authorities/acme/update", - "api-reference/endpoints/certificate-authorities/acme/delete" - ] - }, - { - "group": "Internal", - "pages": [ - "api-reference/endpoints/certificate-authorities/internal/list", - "api-reference/endpoints/certificate-authorities/internal/create", - "api-reference/endpoints/certificate-authorities/internal/read", - "api-reference/endpoints/certificate-authorities/internal/update", - "api-reference/endpoints/certificate-authorities/internal/delete" - ] - }, - "api-reference/endpoints/certificate-authorities/list", - "api-reference/endpoints/certificate-authorities/create", - "api-reference/endpoints/certificate-authorities/read", - "api-reference/endpoints/certificate-authorities/update", - "api-reference/endpoints/certificate-authorities/delete", - "api-reference/endpoints/certificate-authorities/renew", - "api-reference/endpoints/certificate-authorities/list-ca-certs", - "api-reference/endpoints/certificate-authorities/csr", - "api-reference/endpoints/certificate-authorities/cert", - "api-reference/endpoints/certificate-authorities/sign-intermediate", - "api-reference/endpoints/certificate-authorities/import-cert", - "api-reference/endpoints/certificate-authorities/issue-cert", - "api-reference/endpoints/certificate-authorities/sign-cert", - "api-reference/endpoints/certificate-authorities/crl" - ] - }, - { - "group": "Certificates", - "pages": [ - "api-reference/endpoints/certificates/list", - "api-reference/endpoints/certificates/read", - "api-reference/endpoints/certificates/revoke", - "api-reference/endpoints/certificates/delete", - "api-reference/endpoints/certificates/cert-body", - "api-reference/endpoints/certificates/bundle", - "api-reference/endpoints/certificates/private-key", - "api-reference/endpoints/certificates/issue-certificate", - "api-reference/endpoints/certificates/sign-certificate" - ] - }, - { - "group": "Certificate Templates", - "pages": [ - "api-reference/endpoints/certificate-templates/create", - "api-reference/endpoints/certificate-templates/update", - "api-reference/endpoints/certificate-templates/get-by-id", - "api-reference/endpoints/certificate-templates/delete" - ] - }, - { - "group": "Certificate Collections", - "pages": [ - "api-reference/endpoints/pki-collections/create", - "api-reference/endpoints/pki-collections/read", - "api-reference/endpoints/pki-collections/update", - "api-reference/endpoints/pki-collections/delete", - "api-reference/endpoints/pki-collections/add-item", - "api-reference/endpoints/pki-collections/list-items", - "api-reference/endpoints/pki-collections/delete-item" - ] - }, - { - "group": "PKI Alerting", - "pages": [ - "api-reference/endpoints/pki-alerts/create", - "api-reference/endpoints/pki-alerts/read", - "api-reference/endpoints/pki-alerts/update", - "api-reference/endpoints/pki-alerts/delete" - ] - } - ] - }, - { - "group": "Infisical SSH", - "pages": [ - { - "group": "Hosts", - "pages": [ - "api-reference/endpoints/ssh/hosts/list-my", - "api-reference/endpoints/ssh/hosts/list", - "api-reference/endpoints/ssh/hosts/create", - "api-reference/endpoints/ssh/hosts/read", - "api-reference/endpoints/ssh/hosts/update", - "api-reference/endpoints/ssh/hosts/delete", - "api-reference/endpoints/ssh/hosts/issue-host-cert", - "api-reference/endpoints/ssh/hosts/issue-user-cert", - "api-reference/endpoints/ssh/hosts/read-user-ca-pk", - "api-reference/endpoints/ssh/hosts/read-host-ca-pk" - ] - }, - { - "group": "Host Groups", - "pages": [ - "api-reference/endpoints/ssh/groups/list", - "api-reference/endpoints/ssh/groups/create", - "api-reference/endpoints/ssh/groups/read", - "api-reference/endpoints/ssh/groups/update", - "api-reference/endpoints/ssh/groups/delete", - "api-reference/endpoints/ssh/groups/add-host", - "api-reference/endpoints/ssh/groups/list-hosts", - "api-reference/endpoints/ssh/groups/remove-host" - ] - }, - { - "group": "Certificates", - "pages": [ - "api-reference/endpoints/ssh/certificates/issue-credentials", - "api-reference/endpoints/ssh/certificates/sign-key" - ] - }, - { - "group": "Certificate Authorities", - "pages": [ - "api-reference/endpoints/ssh/ca/list", - "api-reference/endpoints/ssh/ca/create", - "api-reference/endpoints/ssh/ca/read", - "api-reference/endpoints/ssh/ca/update", - "api-reference/endpoints/ssh/ca/delete", - "api-reference/endpoints/ssh/ca/public-key", - "api-reference/endpoints/ssh/ca/list-certificate-templates" - ] - }, - { - "group": "Certificate Templates", - "pages": [ - "api-reference/endpoints/ssh/certificate-templates/list", - "api-reference/endpoints/ssh/certificate-templates/create", - "api-reference/endpoints/ssh/certificate-templates/read", - "api-reference/endpoints/ssh/certificate-templates/update", - "api-reference/endpoints/ssh/certificate-templates/delete" - ] - } - ] - }, - { - "group": "Infisical KMS", - "pages": [ - { - "group": "Keys", - "pages": [ - "api-reference/endpoints/kms/keys/list", - "api-reference/endpoints/kms/keys/get-by-id", - "api-reference/endpoints/kms/keys/get-by-name", - "api-reference/endpoints/kms/keys/create", - "api-reference/endpoints/kms/keys/update", - "api-reference/endpoints/kms/keys/delete" - ] - }, - { - "group": "Encryption", - "pages": [ - "api-reference/endpoints/kms/encryption/encrypt", - "api-reference/endpoints/kms/encryption/decrypt" - ] - }, - { - "group": "Signing", - "pages": [ - "api-reference/endpoints/kms/signing/sign", - "api-reference/endpoints/kms/signing/verify", - "api-reference/endpoints/kms/signing/public-key", - "api-reference/endpoints/kms/signing/signing-algorithms" - ] - } - ] - }, - { - "group": "Internals", - "pages": [ - "internals/overview", - { - "group": "Permissions", - "pages": [ - "internals/permissions/overview", - "internals/permissions/project-permissions", - "internals/permissions/organization-permissions", - "internals/permissions/migration" - ] - }, - "internals/components", - "internals/security", - "internals/service-tokens" - ] - }, - { - "group": "", - "pages": ["changelog/overview"] - }, - { - "group": "Contributing", - "pages": [ - { - "group": "Getting Started", - "pages": [ - "contributing/getting-started/overview", - "contributing/getting-started/code-of-conduct", - "contributing/getting-started/pull-requests", - "contributing/getting-started/faq" - ] - }, - { - "group": "Contributing to platform", - "pages": [ - "contributing/platform/developing", - "contributing/platform/backend/how-to-create-a-feature", - "contributing/platform/backend/folder-structure" - ] - }, - { - "group": "Contributing to SDK", - "pages": ["contributing/sdk/developing"] - } - ] - } - ], - "analytics": { - "koala": { - "publicApiKey": "pk_b50d7184e0e39ddd5cdb43cf6abeadd9b97d" - } - }, - "footer": { - "socials": { - "x": "https://www.twitter.com/infisical/", - "linkedin": "https://www.linkedin.com/company/infisical/", - "github": "https://github.com/Infisical/infisical-cli", - "slack": "https://infisical.com/slack" - }, - "links": [ - { - "title": "PRODUCT", - "links": [ - { - "label": "Secret Management", - "url": "https://infisical.com/" - }, - { - "label": "Secret Scanning", - "url": "https://infisical.com/radar" - }, - { - "label": "Share Secrets", - "url": "https://app.infisical.com/share-secret" - }, - { - "label": "Pricing", - "url": "https://infisical.com/pricing" - }, - { - "label": "Security", - "url": "https://infisical.com/docs/internals/security" - }, - { - "label": "Blog", - "url": "https://infisical.com/blog" - }, - { - "label": "Infisical vs Vault", - "url": "https://infisical.com/infisical-vs-hashicorp-vault" - }, - { - "label": "Forum", - "url": "https://questions.infisical.com/" - } - ] - }, - { - "title": "USE CASES", - "links": [ - { - "label": "Infisical Agent", - "url": "https://infisical.com/docs/documentation/getting-started/introduction" - }, - { - "label": "Kubernetes", - "url": "https://infisical.com/docs/integrations/platforms/kubernetes" - }, - { - "label": "Dynamic Secrets", - "url": "https://infisical.com/docs/documentation/platform/dynamic-secrets/overview" - }, - { - "label": "Terraform", - "url": "https://infisical.com/docs/integrations/frameworks/terraform" - }, - { - "label": "Ansible", - "url": "https://infisical.com/docs/integrations/platforms/ansible" - }, - { - "label": "Jenkins", - "url": "https://infisical.com/docs/integrations/cicd/jenkins" - }, - { - "label": "Docker", - "url": "https://infisical.com/docs/integrations/platforms/docker-intro" - }, - { - "label": "AWS ECS", - "url": "https://infisical.com/docs/integrations/platforms/ecs-with-agent" - }, - { - "label": "GitLab", - "url": "https://infisical.com/docs/integrations/cicd/gitlab" - }, - { - "label": "GitHub", - "url": "https://infisical.com/docs/integrations/cicd/githubactions" - }, - { - "label": "SDK", - "url": "https://infisical.com/docs/sdks/overview" - } - ] - }, - { - "title": "DEVELOPERS", - "links": [ - { - "label": "Changelog", - "url": "https://www.infisical.com/docs/changelog" - }, - { - "label": "Status", - "url": "https://status.infisical.com/" - }, - { - "label": "Feedback & Requests", - "url": "https://github.com/Infisical/infisical/issues" - }, - { - "label": "Trust of Center", - "url": "https://app.vanta.com/infisical.com/trust/hoop8cr78cuarxo9sztvs" - }, - { - "label": "Open Source Friends", - "url": "https://infisical.com/infisical-friends" - }, - { - "label": "How to contribute", - "url": "https://www.infisical.com/infisical-heroes" - } - ] - }, - { - "title": "OTHERS", - "links": [ - { - "label": "Customers", - "url": "https://infisical.com/customers/traba" - }, - { - "label": "Company Handbook", - "url": "https://infisical.com/wiki/handbook/overview" - }, - { - "label": "Careers", - "url": "https://infisical.com/careers" - }, - { - "label": "Terms of Service", - "url": "https://infisical.com/terms" - }, - { - "label": "Privacy Policy", - "url": "https://infisical.com/privacy" - }, - { - "label": "Subprocessors", - "url": "https://infisical.com/subprocessors" - }, - { - "label": "SLA", - "url": "https://infisical.com/sla" - }, - { - "label": "Team Email", - "url": "mailto:team@infisical.com" - }, - { - "label": "Sales", - "url": "mailto:sales@infisical.com" - }, - { - "label": "Support", - "url": "https://infisical.com/slack" - } - ] - } - ] - } -} diff --git a/docs/self-hosting/configuration/envars.mdx b/docs/self-hosting/configuration/envars.mdx index 90f3b2207..5e9121e12 100644 --- a/docs/self-hosting/configuration/envars.mdx +++ b/docs/self-hosting/configuration/envars.mdx @@ -794,3 +794,9 @@ If export type is set to `otlp`, you will have to configure a value for `OTEL_EX The TLS header used to propagate the client certificate from the load balancer to the server. + +## Environment Variable Overrides + +If you can't directly access and modify environment variables, you can update them using the [Server Admin Console](/documentation/platform/admin-panel/server-admin). + +![Environment Variables Overrides Page](../../images/self-hosting/configuration/overrides/page.png) diff --git a/frontend/public/images/integrations/Zabbix.png b/frontend/public/images/integrations/Zabbix.png new file mode 100644 index 000000000..3ac67d2b4 Binary files /dev/null and b/frontend/public/images/integrations/Zabbix.png differ diff --git a/frontend/public/lotties/infisical_loading.json b/frontend/public/lotties/infisical_loading.json new file mode 100644 index 000000000..f3ad78900 --- /dev/null +++ b/frontend/public/lotties/infisical_loading.json @@ -0,0 +1 @@ +{"v":"5.7.5","fr":100,"ip":0,"op":300,"w":800,"h":419,"nm":"Comp 1","ddd":0,"metadata":{},"assets":[{"id":"0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Vector (Stroke)","sr":1,"ks":{"p":{"a":0,"k":[-205,-431],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","nm":"Vector (Stroke)","it":[{"ty":"gr","nm":"Path 1","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[20.447,-130.148],[-77.721,-88.116],[-139.588,-16.199],[-148.936,-1.772],[-139.006,12.261],[-71.202,92.517],[20.447,130.148],[148.936,0],[20.447,-130.148]],"i":[[0,0],[29.018,-24.696],[16.327,-25.197],[0,0],[0,0],[-23.672,-20.706],[-35.524,0],[0,71.767],[71.074,0]],"o":[[-35.542,0],[-24.403,20.768],[0,0],[0,0],[22.425,31.688],[26.864,23.498],[71.074,0],[0,-71.768],[0,0]]}}},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]},{"ty":"gr","nm":"Path 2","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[-61.273,-68.789],[-77.721,-88.116],[-61.273,-68.789]],"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]]}}},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]},{"ty":"gr","nm":"Path 3","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[-44.825,-49.463],[20.447,-79.392],[98.18,0],[20.447,79.392],[-37.785,54.313],[-87.278,-2.751],[-44.825,-49.463],[-44.825,-49.463]],"i":[[0,0],[-18.899,0],[0,-44.371],[42.411,0],[20.197,17.666],[17.78,24.244],[-15.565,13.246],[0,0]],"o":[[24.072,-20.487],[42.411,0],[0,44.371],[-20.186,0],[-15.87,-13.881],[12.425,-17.11],[0,0],[0,0]]}}},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]},{"ty":"gf","o":{"a":0,"k":100,"ix":2},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.9215686274509803,0.9450980392156862,0.4235294117647059,0.5,0.19215686274509805,0.20392156862745098,0.09019607843137255,1,0,0,0,0,1,0.5,1,1,0],"ix":2}},"s":{"a":0,"k":[-84.7012710571289,-1.981994867324829],"ix":2},"e":{"a":0,"k":[550.908447265625,-0.7451161742210388],"ix":2},"t":1},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]},{"ty":"tr","p":{"a":0,"k":[747.4509887695312,615.6649780273438],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[140.5150055885315,140.5150055885315],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":301,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Vector (Stroke)","sr":1,"ks":{"p":{"a":0,"k":[-205,-431],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","nm":"Vector (Stroke)","it":[{"ty":"gr","nm":"Path 1","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[-98.468,0],[-20.444,-79.392],[46.888,-43.887],[46.888,-43.887],[87.062,-0.165],[48.675,41.472],[65.75,60.248],[48.675,41.472],[-20.444,79.392],[-98.468,0]],"i":[[0,0],[-42.675,0],[-27.224,-24.453],[0,0],[-11.056,-14.336],[14.986,-13.628],[0,0],[0,0],[15.764,0],[0,44.267]],"o":[[0,-44.266],[16.468,0],[0,0],[15.303,13.745],[-10.159,13.06],[0,0],[0,0],[-28.618,26.025],[-42.675,0],[0,0]]}}},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]},{"ty":"gr","nm":"Path 2","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[-20.444,-130.149],[-149.224,0],[-20.444,130.149],[82.824,79.024],[82.824,79.023],[139.462,14.105],[149.224,-0.255],[139.377,-14.556],[80.805,-81.647],[-20.444,-130.149]],"i":[[0,0],[0,-71.764],[-71.238,0],[-30.616,27.842],[0,0],[-12.465,18.334],[0,0],[0,0],[24.224,21.758],[35.661,0]],"o":[[-71.238,0],[0,71.764],[35.674,0],[0,0],[24.139,-21.952],[0,0],[0,0],[-13.86,-20.129],[-29.959,-26.91],[0,0]]}}},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]},{"ty":"gf","o":{"a":0,"k":100,"ix":2},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.9215686274509803,0.9450980392156862,0.4235294117647059,0.5,0.19215686274509805,0.20392156862745098,0.09019607843137255,1,0,0,0,0,1,0.5,1,1,0],"ix":2}},"s":{"a":0,"k":[84.2059555053711,0.10817349702119827],"ix":2},"e":{"a":0,"k":[-540.4896240234375,-5.982279300689697],"ix":2},"t":1},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]},{"ty":"tr","p":{"a":0,"k":[414.8349914550781,614.260009765625],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[140.5150055885315,140.5150055885315],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":301,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}},"ao":0,"shapes":[{"ty":"gr","nm":"Shape Layer 2","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[64.219,60.955],[75.017,50.459],[64.706,38.974],[56.737,41.937],[52.529,45.908],[46.994,53.295],[43.203,57.056],[35.02,60.825],[24.169,49.937],[34.908,38.936],[42.707,42.246],[47.079,46.558],[64.219,60.955]],"i":[[0,0],[-0.155,3.832],[5.611,0.221],[2.488,-1.995],[1.254,-1.476],[2.012,-2.367],[1.531,-1.317],[2.721,0.003],[0,5.642],[-6.195,0.163],[-2.204,-1.755],[-1.435,-1.717],[-7.72,0.139]],"o":[[7.629,-0.137],[0.248,-6.111],[-2.688,-0.106],[-1.511,1.212],[-2.012,2.368],[-1.153,1.356],[-3.063,2.455],[-6.495,-0.007],[0,-5.642],[2.954,-0.078],[1.264,1.149],[4.489,5.371],[0,0]]}}},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":20,"ix":2},"o":{"a":1,"k":[{"t":0,"s":[61.00000000000001],"i":{"x":[0.833],"y":[0.904]},"o":{"x":[0.167],"y":[0.167]}},{"t":50,"s":[149],"i":{"x":[0.833],"y":[0.715]},"o":{"x":[0.167],"y":[0.258]}},{"t":153,"s":[216.99999999999997],"i":{"x":[0.833],"y":[0.889]},"o":{"x":[0.167],"y":[0.092]}},{"t":207,"s":[325.00000000000006],"i":{"x":[0.833],"y":[0.81]},"o":{"x":[0.167],"y":[0.25]}},{"t":287,"s":[397.00000000000006],"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.095]}},{"t":300,"s":[421.00000000000006],"i":{"x":[0.75],"y":[0.75]},"o":{"x":[0.25],"y":[0.25]}}],"ix":2},"m":1},{"ty":"gs","o":{"a":0,"k":100,"ix":2},"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.9215686274509803,0.9450980392156862,0.4235294117647059,0.5,0.19215686274509805,0.20392156862745098,0.09019607843137255,1,0,0,0,0,1,0.5,1,1,0],"ix":2}},"s":{"a":0,"k":[49.317,61.556],"ix":2},"e":{"a":1,"k":[{"t":0,"s":[131.06,-44.124],"i":{"x":[0.692],"y":[0.616]},"o":{"x":[0.362],"y":[0]}},{"t":67,"s":[76.234,-60.88],"i":{"x":[0.667],"y":[0.906]},"o":{"x":[0.359],"y":[0.608]}},{"t":157,"s":[23.388,-39.908],"i":{"x":[0.654],"y":[0.496]},"o":{"x":[0.328],"y":[0.076]}},{"t":227,"s":[75.71,-53.944],"i":{"x":[0.637],"y":[1]},"o":{"x":[0.31],"y":[0.455]}},{"t":300,"s":[131.06,-44.124],"i":{"x":[0.75],"y":[0.75]},"o":{"x":[0.25],"y":[0.25]}}],"ix":2},"t":1,"w":{"a":0,"k":5.4,"ix":2},"lc":1,"lj":1,"ml":4},{"ty":"tr","p":{"a":0,"k":[400.33172607421875,209.90084838867188],"ix":2},"a":{"a":0,"k":[49.59747009478117,49.94493849689434],"ix":2},"s":{"a":0,"k":[1339.9999618530273,1339.9999618530273],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":301,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":0,"k":[400.0003662109375,209.50125122070312],"ix":2},"a":{"a":0,"k":[580.941,614.963],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}},"ao":0,"ip":0,"op":301,"st":0,"bm":0},{"ddd":0,"refId":"0","w":752,"h":368,"ind":5,"ty":0,"nm":"Vector (Stroke) :M","sr":1,"ks":{"p":{"a":0,"k":[205,431],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":55,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}},"ao":0,"ip":0,"op":301,"st":0,"bm":0,"parent":4}],"markers":[]} \ No newline at end of file diff --git a/frontend/public/lotties/infisical_loading_bw.json b/frontend/public/lotties/infisical_loading_bw.json new file mode 100644 index 000000000..3f9dda4a4 --- /dev/null +++ b/frontend/public/lotties/infisical_loading_bw.json @@ -0,0 +1 @@ +{"v":"5.7.5","fr":100,"ip":0,"op":300,"w":800,"h":420,"nm":"Comp 1","ddd":0,"metadata":{},"assets":[{"id":"0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Vector (Stroke)","sr":1,"ks":{"p":{"a":0,"k":[-205,-431],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","nm":"Vector (Stroke)","it":[{"ty":"gr","nm":"Path 1","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[20.447,-130.148],[-77.721,-88.116],[-139.588,-16.199],[-148.936,-1.772],[-139.006,12.261],[-71.202,92.517],[20.447,130.148],[148.936,0],[20.447,-130.148]],"i":[[0,0],[29.018,-24.696],[16.327,-25.197],[0,0],[0,0],[-23.672,-20.706],[-35.524,0],[0,71.767],[71.074,0]],"o":[[-35.542,0],[-24.403,20.768],[0,0],[0,0],[22.425,31.688],[26.864,23.498],[71.074,0],[0,-71.768],[0,0]]}}},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]},{"ty":"gr","nm":"Path 2","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[-61.273,-68.789],[-77.721,-88.116],[-61.273,-68.789]],"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]]}}},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]},{"ty":"gr","nm":"Path 3","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[-44.825,-49.463],[20.447,-79.392],[98.18,0],[20.447,79.392],[-37.785,54.313],[-87.278,-2.751],[-44.825,-49.463],[-44.825,-49.463]],"i":[[0,0],[-18.899,0],[0,-44.371],[42.411,0],[20.197,17.666],[17.78,24.244],[-15.565,13.246],[0,0]],"o":[[24.072,-20.487],[42.411,0],[0,44.371],[-20.186,0],[-15.87,-13.881],[12.425,-17.11],[0,0],[0,0]]}}},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]},{"ty":"gf","o":{"a":0,"k":100,"ix":2},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0,0,0,0.5,0,0,0,1,0,0,0,0,1,0.5,0.5,1,0],"ix":2}},"s":{"a":0,"k":[-86.28473663330078,1.4994840621948242],"ix":2},"e":{"a":0,"k":[462.44384765625,-5.23858642578125],"ix":2},"t":1},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]},{"ty":"tr","p":{"a":0,"k":[747.4509887695312,615.6649780273438],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[140.5150055885315,140.5150055885315],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":301,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Vector (Stroke)","sr":1,"ks":{"p":{"a":0,"k":[-205,-431],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","nm":"Vector (Stroke)","it":[{"ty":"gr","nm":"Path 1","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[-98.468,0],[-20.444,-79.392],[46.888,-43.887],[46.888,-43.887],[87.062,-0.165],[48.675,41.472],[65.75,60.248],[48.675,41.472],[-20.444,79.392],[-98.468,0]],"i":[[0,0],[-42.675,0],[-27.224,-24.453],[0,0],[-11.056,-14.336],[14.986,-13.628],[0,0],[0,0],[15.764,0],[0,44.267]],"o":[[0,-44.266],[16.468,0],[0,0],[15.303,13.745],[-10.159,13.06],[0,0],[0,0],[-28.618,26.025],[-42.675,0],[0,0]]}}},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]},{"ty":"gr","nm":"Path 2","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[-20.444,-130.149],[-149.224,0],[-20.444,130.149],[82.824,79.024],[82.824,79.023],[139.462,14.105],[149.224,-0.255],[139.377,-14.556],[80.805,-81.647],[-20.444,-130.149]],"i":[[0,0],[0,-71.764],[-71.238,0],[-30.616,27.842],[0,0],[-12.465,18.334],[0,0],[0,0],[24.224,21.758],[35.661,0]],"o":[[-71.238,0],[0,71.764],[35.674,0],[0,0],[24.139,-21.952],[0,0],[0,0],[-13.86,-20.129],[-29.959,-26.91],[0,0]]}}},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]},{"ty":"gf","o":{"a":0,"k":100,"ix":2},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0,0,0,0.5,0,0,0,1,0,0,0,0,1,0.5,0.5,1,0],"ix":2}},"s":{"a":0,"k":[62.10511016845703,5.982990741729736],"ix":2},"e":{"a":0,"k":[-461.3863220214844,-3.3000035285949707],"ix":2},"t":1},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]},{"ty":"tr","p":{"a":0,"k":[414.8349914550781,614.260009765625],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[140.5150055885315,140.5150055885315],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":301,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}},"ao":0,"shapes":[{"ty":"gr","nm":"Shape Layer 2","it":[{"ty":"sh","d":1,"ks":{"a":0,"k":{"c":true,"v":[[64.219,60.955],[75.017,50.459],[64.706,38.974],[56.737,41.937],[52.529,45.908],[46.994,53.295],[43.203,57.056],[35.02,60.825],[24.169,49.937],[34.908,38.936],[42.707,42.246],[47.079,46.558],[64.219,60.955]],"i":[[0,0],[-0.155,3.832],[5.611,0.221],[2.488,-1.995],[1.254,-1.476],[2.012,-2.367],[1.531,-1.317],[2.721,0.003],[0,5.642],[-6.195,0.163],[-2.204,-1.755],[-1.435,-1.717],[-7.72,0.139]],"o":[[7.629,-0.137],[0.248,-6.111],[-2.688,-0.106],[-1.511,1.212],[-2.012,2.368],[-1.153,1.356],[-3.063,2.455],[-6.495,-0.007],[0,-5.642],[2.954,-0.078],[1.264,1.149],[4.489,5.371],[0,0]]}}},{"ty":"tm","s":{"a":0,"k":0,"ix":2},"e":{"a":0,"k":20,"ix":2},"o":{"a":1,"k":[{"t":0,"s":[61.00000000000001],"i":{"x":[0.833],"y":[0.904]},"o":{"x":[0.167],"y":[0.167]}},{"t":50,"s":[149],"i":{"x":[0.833],"y":[0.715]},"o":{"x":[0.167],"y":[0.258]}},{"t":153,"s":[216.99999999999997],"i":{"x":[0.833],"y":[0.889]},"o":{"x":[0.167],"y":[0.092]}},{"t":207,"s":[325.00000000000006],"i":{"x":[0.833],"y":[0.81]},"o":{"x":[0.167],"y":[0.25]}},{"t":287,"s":[397.00000000000006],"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.095]}},{"t":300,"s":[421.00000000000006],"i":{"x":[0.75],"y":[0.75]},"o":{"x":[0.25],"y":[0.25]}}],"ix":2},"m":1},{"ty":"gs","o":{"a":0,"k":100,"ix":2},"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0,0,0,0.5,0,0,0,1,0,0,0,0,1,0.5,0.5,1,0],"ix":2}},"s":{"a":0,"k":[49.759,50.221],"ix":2},"e":{"a":1,"k":[{"t":0,"s":[120.553,-22.385],"i":{"x":[0.692],"y":[0.616]},"o":{"x":[0.362],"y":[0]}},{"t":67,"s":[65.727,-39.14],"i":{"x":[0.682],"y":[1]},"o":{"x":[0.359],"y":[0.551]}},{"t":150,"s":[8.856,-27.571],"i":{"x":[0.653],"y":[0.47]},"o":{"x":[0.329],"y":[0]}},{"t":227,"s":[65.202,-32.204],"i":{"x":[0.637],"y":[1]},"o":{"x":[0.31],"y":[0.455]}},{"t":300,"s":[120.553,-22.385],"i":{"x":[0.75],"y":[0.75]},"o":{"x":[0.25],"y":[0.25]}}],"ix":2},"t":1,"w":{"a":0,"k":5.4,"ix":2},"lc":1,"lj":1,"ml":4},{"ty":"tr","p":{"a":0,"k":[400.3317565917969,210.40078735351562],"ix":2},"a":{"a":0,"k":[49.59747009478117,49.94493849689434],"ix":2},"s":{"a":0,"k":[1339.9999618530273,1339.9999618530273],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}}]}],"ip":0,"op":301,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":3,"nm":"","sr":1,"ks":{"p":{"a":0,"k":[400.0003356933594,210.00125122070312],"ix":2},"a":{"a":0,"k":[580.941,614.963],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":100,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}},"ao":0,"ip":0,"op":301,"st":0,"bm":0},{"ddd":0,"refId":"0","w":752,"h":368,"ind":5,"ty":0,"nm":"Vector (Stroke) :M","sr":1,"ks":{"p":{"a":0,"k":[205,431],"ix":2},"a":{"a":0,"k":[0,0],"ix":2},"s":{"a":0,"k":[100,100],"ix":2},"r":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":33,"ix":2},"sk":{"a":0,"k":0,"ix":2},"sa":{"a":0,"k":0,"ix":2}},"ao":0,"ip":0,"op":301,"st":0,"bm":0,"parent":4}],"markers":[]} \ No newline at end of file diff --git a/frontend/public/lotties/terminal.json b/frontend/public/lotties/terminal.json new file mode 100644 index 000000000..9a6228cfa --- /dev/null +++ b/frontend/public/lotties/terminal.json @@ -0,0 +1,365 @@ +{ + "v": "5.7.5", + "fr": 100, + "ip": 0, + "op": 100, + "w": 22, + "h": 20, + "nm": "Comp 1", + "ddd": 0, + "metadata": {}, + "assets": [ + { + "id": "0", + "layers": [ + { + "ddd": 0, + "ind": 1, + "ty": 4, + "nm": "Path 1", + "sr": 1, + "ks": { + "p": { "a": 0, "k": [-6, -6], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "nm": "Path 1", + "it": [ + { + "ty": "sh", + "d": 1, + "ks": { + "a": 0, + "k": { + "c": false, + "v": [ + [9.25, 9.25], + [12.25, 9.25] + ], + "i": [ + [0, 0], + [0, 0] + ], + "o": [ + [0, 0], + [0, 0] + ] + } + } + }, + { + "ty": "st", + "c": { "a": 0, "k": [1, 1, 1], "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "w": { "a": 0, "k": 1.5, "ix": 2 }, + "lc": 2, + "lj": 2, + "ml": 4 + }, + { + "ty": "tr", + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + } + ], + "ip": 0, + "op": 101, + "st": 0, + "bm": 0 + } + ] + } + ], + "layers": [ + { + "ddd": 0, + "ind": 2, + "ty": 3, + "nm": "", + "sr": 1, + "ks": { + "p": { "a": 0, "k": [0.7886505126953125, 0.7357635498046875], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + }, + "ao": 0, + "ip": 0, + "op": 101, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "refId": "0", + "w": 10, + "h": 7, + "ind": 3, + "ty": 0, + "nm": "10 Outlines 2", + "sr": 1, + "ks": { + "p": { "a": 0, "k": [6, 6], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { + "a": 1, + "k": [ + { "t": 0, "s": [100], "i": { "x": [0.667], "y": [1] }, "o": { "x": [1], "y": [0] } }, + { "t": 50, "s": [0], "i": { "x": [0.667], "y": [1] }, "o": { "x": [1], "y": [0] } }, + { + "t": 100, + "s": [100], + "i": { "x": [0.75], "y": [0.75] }, + "o": { "x": [0.25], "y": [0.25] } + } + ], + "ix": 2 + }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + }, + "ao": 0, + "ip": 0, + "op": 101, + "st": 0, + "bm": 0, + "parent": 2 + }, + { + "ddd": 0, + "ind": 4, + "ty": 4, + "nm": "10 Outlines", + "sr": 1, + "ks": { + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ty": "gr", + "it": [ + { + "ty": "gr", + "nm": "Path 1", + "it": [ + { + "ty": "sh", + "d": 1, + "ks": { + "a": 0, + "k": { + "c": false, + "v": [ + [4.75, 4.75], + [7.75, 7], + [4.75, 9.25] + ], + "i": [ + [0, 0], + [0, 0], + [0, 0] + ], + "o": [ + [0, 0], + [0, 0], + [0, 0] + ] + } + } + }, + { + "ty": "st", + "c": { "a": 0, "k": [1, 1, 1], "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "w": { "a": 0, "k": 1, "ix": 2 }, + "lc": 2, + "lj": 2, + "ml": 4 + }, + { + "ty": "tr", + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "tr", + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "gr", + "it": [ + { + "ty": "gr", + "nm": "Group", + "it": [ + { + "ty": "gr", + "nm": "Path 2", + "it": [ + { + "ty": "sh", + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "v": [ + [3.25, 17.5], + [16.75, 17.5], + [18.341, 16.841], + [19, 15.25], + [19, 3.25], + [18.341, 1.659], + [16.75, 1], + [3.25, 1], + [1.659, 1.659], + [1, 3.25], + [1, 15.25], + [1.659, 16.841], + [3.25, 17.5] + ], + "i": [ + [0, 0], + [0, 0], + [-0.422, 0.422], + [0, 0.597], + [0, 0], + [0.422, 0.422], + [0.597, 0], + [0, 0], + [0.422, -0.422], + [0, -0.597], + [0, 0], + [-0.422, -0.422], + [-0.597, 0] + ], + "o": [ + [0, 0], + [0.597, 0], + [0.422, -0.422], + [0, 0], + [0, -0.597], + [-0.422, -0.422], + [0, 0], + [-0.597, 0], + [-0.422, 0.422], + [0, 0], + [0, 0.597], + [0.422, 0.422], + [0, 0] + ] + } + } + }, + { + "ty": "tr", + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "st", + "c": { "a": 0, "k": [1, 1, 1], "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "w": { "a": 0, "k": 1.5, "ix": 2 }, + "lc": 2, + "lj": 2, + "ml": 4 + }, + { + "ty": "tr", + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "tr", + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "tr", + "p": { "a": 0, "k": [0.7886505126953125, 0.7357635498046875], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + } + ], + "ip": 0, + "op": 101, + "st": 0, + "bm": 0 + } + ], + "markers": [] +} + diff --git a/frontend/public/lotties/vault.json b/frontend/public/lotties/vault.json new file mode 100644 index 000000000..13f1ab507 --- /dev/null +++ b/frontend/public/lotties/vault.json @@ -0,0 +1,992 @@ +{ + "v": "5.7.5", + "fr": 100, + "ip": 0, + "op": 200, + "w": 500, + "h": 500, + "nm": "Comp 1", + "ddd": 0, + "metadata": {}, + "assets": [], + "layers": [ + { + "ddd": 0, + "ind": 1, + "ty": 4, + "nm": "Shape Layer 2", + "sr": 1, + "ks": { + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "nm": "Shape Layer 2", + "it": [ + { + "ty": "rc", + "d": 1, + "s": { "a": 0, "k": [49.3180325191623, 68.7788286222386], "ix": 2 }, + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 } + }, + { + "ty": "fl", + "c": { "a": 0, "k": [1, 1, 1], "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "r": 1, + "bm": 0 + }, + { + "ty": "tr", + "p": { "a": 0, "k": [36.65843963623047, 374.4748840332031], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + } + ], + "ip": 0, + "op": 201, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 2, + "ty": 4, + "nm": "Shape Layer 1", + "sr": 1, + "ks": { + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "nm": "Shape Layer 1", + "it": [ + { + "ty": "rc", + "d": 1, + "s": { "a": 0, "k": [49.3180325191623, 68.7788286222386], "ix": 2 }, + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 } + }, + { + "ty": "fl", + "c": { "a": 0, "k": [1, 1, 1], "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "r": 1, + "bm": 0 + }, + { + "ty": "tr", + "p": { "a": 0, "k": [36.65843963623047, 152], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + } + ], + "ip": 0, + "op": 201, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 3, + "ty": 4, + "nm": "bank-safe-box-svgrepo-com.svg 1", + "sr": 1, + "ks": { + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ty": "gr", + "nm": "bank-safe-box-svgrepo-com.svg 1", + "it": [ + { + "ty": "gr", + "nm": "Path", + "it": [ + { + "ty": "gr", + "nm": "Path", + "it": [ + { + "ty": "sh", + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "v": [ + [33.06575776130341, 49.665937523501114], + [20.000300594090934, 36.60048035628864], + [33.06575776130341, 23.530797074065426], + [46.12614359050298, 36.60048035628864], + [33.06575776130341, 49.665937523501114], + [33.06575776130341, 49.665937523501114] + ], + "i": [ + [0, 0], + [0, 7.20383564731219], + [-7.208906985325083, 0], + [0, -7.208061762322929], + [7.1996095323014515, 0], + [0, 0] + ], + "o": [ + [-7.20383564731219, 0], + [0, -7.20383564731219], + [7.1996095323014515, 0], + [0, 7.1996095323014515], + [0, 0], + [0, 0] + ] + } + } + }, + { + "ty": "tr", + "p": { "a": 0, "k": [-45.124942779541016, -76.91899108886719], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 1.36079623004602e-7, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "gr", + "nm": "Path", + "it": [ + { + "ty": "sh", + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "v": [ + [56.59403564377824, 2.417355107683346], + [49.427568347046794, 2.417355107683346], + [38.852034318840836, 13.001059012587813], + [29.055609441248567, 10.541183410268967], + [19.84890111807693, 12.677977524964414], + [8.650971028656253, 1.4845037319247485], + [1.4845037319248013, 1.4845037319247485], + [1.4845037319248013, 8.650971028656201], + [11.935261461462396, 19.101728758193797], + [7.4351448327033705, 32.15793443849666], + [11.935261461462396, 45.210426538482], + [1.5253531154174096, 55.624791180908], + [1.5253531154174096, 62.78680218125844], + [5.108215405751383, 64.27371974038958], + [8.691077696085358, 62.78680218125844], + [19.84444482169591, 51.63343505564789], + [29.051153144867545, 53.76651559002584], + [39.33182889586317, 51.04371850122726], + [49.17653031758308, 60.89213350326467], + [52.75939260791704, 62.3745947660148], + [56.34225489825104, 60.89213350326467], + [56.34225489825104, 53.72566620653322], + [46.913474472092304, 44.296885780374474], + [50.66196244458722, 32.15347814211567], + [46.56959693469084, 19.59786308861321], + [56.58363761888923, 9.583822404414821], + [56.59403564377824, 2.417355107683346], + [56.59403564377824, 2.417355107683346] + ], + "i": [ + [0, 0], + [1.979338309233005, -1.9756247289154982], + [3.5251780094019862, -3.527901301634823], + [3.5427556229048607, 0], + [2.8037531397203104, -1.333175333986213], + [3.732643363140226, 3.731157931013225], + [1.9793383092330157, -1.9793383092330104], + [-1.9793383092330157, -1.9793383092330157], + [-3.483585909845862, -3.483585909845862], + [0, -4.9204939207012], + [-2.779243509624741, -3.6363378469061254], + [3.469969448681659, -3.4714548808086705], + [-1.9793383092330157, -1.979338309233005], + [-1.296782246874607, 0], + [-0.9892977965847497, 0.9900405126482554], + [-3.7177890418701867, 3.717789041870177], + [-3.308800062901711, 0], + [-3.0696454904540436, 1.6807664517051837], + [-3.281567140573295, -3.2828050006791307], + [-1.2967822468746175, 0], + [-0.9892977965847708, 0.9892977965847708], + [1.979338309233005, 1.979338309233005], + [3.142926808719575, 3.1429268087195856], + [0, 4.496403048441521], + [2.5490015299390887, 3.5472119192858735], + [-3.3380135613994684, 3.3380135613994586], + [1.979338309233005, 1.983794605614028], + [0, 0] + ], + "o": [ + [-1.979338309233005, -1.9756247289154982], + [-3.5251780094019756, 3.527901301634823], + [-2.9530390684842214, -1.5233106462427428], + [-3.3043437665206987, 0], + [-3.732643363140226, -3.731157931013225], + [-1.9793383092330157, -1.9793383092330104], + [-1.9793383092330157, 1.9793383092330157], + [3.483585909845862, 3.483585909845862], + [-2.779243509624741, 3.6355951308426193], + [0, 4.9204939207012], + [-3.469969448681659, 3.47145488080866], + [-1.9793383092330157, 1.9756247289154982], + [0.9900405126482554, 0.9900405126482554], + [1.296782246874607, 0], + [3.7177890418701867, -3.717789041870177], + [2.7992968433392984, 1.3287190376051898], + [3.741060811859921, 0], + [3.2815671405733053, 3.2828050006791307], + [0.9900405126482554, 0.9900405126482554], + [1.2967822468746175, 0], + [1.979338309233005, -1.979338309233005], + [-3.142926808719575, -3.142926808719575], + [2.3588662176825586, -3.46179957198314], + [0, -4.693965521333064], + [3.3380135613994684, -3.3380135613994586], + [1.9897363341220202, -1.9756247289155193], + [0, 0], + [0, 0] + ] + } + } + }, + { + "ty": "tr", + "p": { "a": 0, "k": [-41.346412658691406, -72.31075286865234], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 1.36079623004602e-7, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "tr", + "p": { "a": 0, "k": [563.65673828125, 513.2783813476562], "ix": 2 }, + "a": { "a": 0, "k": [-12.307790756225586, -40.173892974853516], "ix": 2 }, + "s": { "a": 0, "k": [366.3825750350952, 366.3825750350952], "ix": 2 }, + "r": { + "a": 1, + "k": [ + { + "t": 0, + "s": [0], + "i": { "x": [0.75], "y": [0.75] }, + "o": { "x": [0.25], "y": [0.25] } + }, + { + "t": 100, + "s": [180], + "i": { "x": [0.75], "y": [0.75] }, + "o": { "x": [0.25], "y": [0.25] } + } + ], + "ix": 2 + }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "gr", + "nm": "Path", + "it": [ + { + "ty": "gr", + "nm": "Path", + "it": [ + { + "ty": "sh", + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "v": [ + [16.204001519577492, 10.809684651948828], + [107.78162573450534, 10.809684651948828], + [107.78162573450534, 103.16527938802837], + [16.204001519577492, 103.16527938802837], + [16.204001519577492, 10.809684651948828], + [16.204001519577492, 10.809684651948828] + ], + "i": [ + [0, 0], + [-30.52587473830928, 0], + [0, -30.785198245359847], + [30.52587473830929, 0], + [0, 30.785198245359847], + [0, 0] + ], + "o": [ + [30.52587473830928, 0], + [0, 30.785198245359833], + [-30.52587473830928, 0], + [0, -30.785198245359847], + [0, 0], + [0, 0] + ] + } + } + }, + { + "ty": "tr", + "p": { "a": 0, "k": [-33.81110763549805, -68.36682891845703], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "gr", + "nm": "Path", + "it": [ + { + "ty": "sh", + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "v": [ + [5.187128475098463, 75.41686707022457], + [0.11734912644122932, 75.41686707022457], + [0.11734912644122932, 85.55642576753904], + [5.187128475098463, 85.55642576753904], + [5.187128475098463, 101.79962447177776], + [10.253194243805027, 106.869403820435], + [106.98749566286637, 106.869403820435], + [112.05356143157292, 101.79962447177776], + [112.05356143157292, 5.066065768706561], + [106.98749566286637, 0], + [10.2569078237557, 0], + [5.190842055049137, 5.066065768706561], + [5.190842055049137, 21.277327685369535], + [0, 21.277327685369535], + [0, 31.413172802733335], + [5.190842055049137, 31.413172802733335], + [5.190842055049137, 75.4176097862147], + [5.187128475098463, 75.4176097862147], + [5.187128475098463, 75.41686707022457] + ], + "i": [ + [0, 0], + [1.6899264495524133, 0], + [0, -3.3798528991048267], + [-1.6899264495524133, 0], + [0, -5.414399568079578], + [-2.795582986865779, 0], + [-32.24476713968711, 0], + [0, 2.799296566816458], + [0, 32.244519567690396], + [2.7955829868657895, 0], + [32.243529279703566, 0], + [0, -2.800039282806585], + [0, -5.40375397222099], + [1.7302806850163799, 0], + [0, -3.3786150391212706], + [-1.7302806850163799, 0], + [0, -14.66814566116046], + [0.0012378599835561636, 0], + [0, 0.0002475719967196764] + ], + "o": [ + [-1.6899264495524133, 0], + [0, 3.3798528991048267], + [1.6899264495524107, 0], + [0, 5.414399568079578], + [0, 2.799296566816458], + [32.24476713968713, 0], + [2.799296566816458, 0], + [0, -32.2445195676904], + [0, -2.799296566816452], + [-32.24352927970354, 0], + [-2.799296566816452, 0], + [0, 5.40375397222099], + [-1.7302806850163799, 0], + [0, 3.3786150391212706], + [1.7302806850163799, 0], + [0, 14.66814566116045], + [-0.0012378599835561636, 0], + [0, -0.0002475719967196764], + [0, 0] + ] + } + } + }, + { + "ty": "tr", + "p": { "a": 0, "k": [-30.517711639404297, -64.98767852783203], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "tr", + "p": { "a": 0, "k": [738.9981689453125, 819], "ix": 2 }, + "a": { "a": 0, "k": [75.43499755859375, 71.94499969482422], "ix": 2 }, + "s": { "a": 0, "k": [366.3825750350952, 366.3825750350952], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "gr", + "nm": "Path", + "it": [ + { + "ty": "tr", + "p": { "a": 0, "k": [360.7227783203125, 392.2637023925781], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [454.6020030975342, 454.6020030975342], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "fl", + "c": { "a": 0, "k": [1, 1, 1], "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "r": 1, + "bm": 0 + }, + { + "ty": "tr", + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "gr", + "it": [ + { + "ty": "gr", + "nm": "Path", + "it": [ + { + "ty": "gr", + "nm": "Path", + "it": [ + { + "ty": "sh", + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "v": [ + [33.06575776130341, 49.665937523501114], + [20.000300594090934, 36.60048035628864], + [33.06575776130341, 23.530797074065426], + [46.12614359050298, 36.60048035628864], + [33.06575776130341, 49.665937523501114], + [33.06575776130341, 49.665937523501114] + ], + "i": [ + [0, 0], + [0, 7.20383564731219], + [-7.208906985325083, 0], + [0, -7.208061762322929], + [7.1996095323014515, 0], + [0, 0] + ], + "o": [ + [-7.20383564731219, 0], + [0, -7.20383564731219], + [7.1996095323014515, 0], + [0, 7.1996095323014515], + [0, 0], + [0, 0] + ] + } + } + }, + { + "ty": "tr", + "p": { "a": 0, "k": [-45.124942779541016, -76.91899108886719], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 1.36079623004602e-7, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "gr", + "nm": "Path", + "it": [ + { + "ty": "sh", + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "v": [ + [56.59403564377824, 2.417355107683346], + [49.427568347046794, 2.417355107683346], + [38.852034318840836, 13.001059012587813], + [29.055609441248567, 10.541183410268967], + [19.84890111807693, 12.677977524964414], + [8.650971028656253, 1.4845037319247485], + [1.4845037319248013, 1.4845037319247485], + [1.4845037319248013, 8.650971028656201], + [11.935261461462396, 19.101728758193797], + [7.4351448327033705, 32.15793443849666], + [11.935261461462396, 45.210426538482], + [1.5253531154174096, 55.624791180908], + [1.5253531154174096, 62.78680218125844], + [5.108215405751383, 64.27371974038958], + [8.691077696085358, 62.78680218125844], + [19.84444482169591, 51.63343505564789], + [29.051153144867545, 53.76651559002584], + [39.33182889586317, 51.04371850122726], + [49.17653031758308, 60.89213350326467], + [52.75939260791704, 62.3745947660148], + [56.34225489825104, 60.89213350326467], + [56.34225489825104, 53.72566620653322], + [46.913474472092304, 44.296885780374474], + [50.66196244458722, 32.15347814211567], + [46.56959693469084, 19.59786308861321], + [56.58363761888923, 9.583822404414821], + [56.59403564377824, 2.417355107683346], + [56.59403564377824, 2.417355107683346] + ], + "i": [ + [0, 0], + [1.979338309233005, -1.9756247289154982], + [3.5251780094019862, -3.527901301634823], + [3.5427556229048607, 0], + [2.8037531397203104, -1.333175333986213], + [3.732643363140226, 3.731157931013225], + [1.9793383092330157, -1.9793383092330104], + [-1.9793383092330157, -1.9793383092330157], + [-3.483585909845862, -3.483585909845862], + [0, -4.9204939207012], + [-2.779243509624741, -3.6363378469061254], + [3.469969448681659, -3.4714548808086705], + [-1.9793383092330157, -1.979338309233005], + [-1.296782246874607, 0], + [-0.9892977965847497, 0.9900405126482554], + [-3.7177890418701867, 3.717789041870177], + [-3.308800062901711, 0], + [-3.0696454904540436, 1.6807664517051837], + [-3.281567140573295, -3.2828050006791307], + [-1.2967822468746175, 0], + [-0.9892977965847708, 0.9892977965847708], + [1.979338309233005, 1.979338309233005], + [3.142926808719575, 3.1429268087195856], + [0, 4.496403048441521], + [2.5490015299390887, 3.5472119192858735], + [-3.3380135613994684, 3.3380135613994586], + [1.979338309233005, 1.983794605614028], + [0, 0] + ], + "o": [ + [-1.979338309233005, -1.9756247289154982], + [-3.5251780094019756, 3.527901301634823], + [-2.9530390684842214, -1.5233106462427428], + [-3.3043437665206987, 0], + [-3.732643363140226, -3.731157931013225], + [-1.9793383092330157, -1.9793383092330104], + [-1.9793383092330157, 1.9793383092330157], + [3.483585909845862, 3.483585909845862], + [-2.779243509624741, 3.6355951308426193], + [0, 4.9204939207012], + [-3.469969448681659, 3.47145488080866], + [-1.9793383092330157, 1.9756247289154982], + [0.9900405126482554, 0.9900405126482554], + [1.296782246874607, 0], + [3.7177890418701867, -3.717789041870177], + [2.7992968433392984, 1.3287190376051898], + [3.741060811859921, 0], + [3.2815671405733053, 3.2828050006791307], + [0.9900405126482554, 0.9900405126482554], + [1.2967822468746175, 0], + [1.979338309233005, -1.979338309233005], + [-3.142926808719575, -3.142926808719575], + [2.3588662176825586, -3.46179957198314], + [0, -4.693965521333064], + [3.3380135613994684, -3.3380135613994586], + [1.9897363341220202, -1.9756247289155193], + [0, 0], + [0, 0] + ] + } + } + }, + { + "ty": "tr", + "p": { "a": 0, "k": [-41.346412658691406, -72.31075286865234], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 1.36079623004602e-7, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "fl", + "c": { "a": 0, "k": [0.00392156862745098, 0, 0.00784313725490196], "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "r": 1, + "bm": 0, + "hd": true + }, + { + "ty": "tr", + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "tr", + "p": { "a": 0, "k": [563.65673828125, 513.2783813476562], "ix": 2 }, + "a": { "a": 0, "k": [-12.307790756225586, -40.173892974853516], "ix": 2 }, + "s": { "a": 0, "k": [366.3825750350952, 366.3825750350952], "ix": 2 }, + "r": { + "a": 1, + "k": [ + { + "t": 0, + "s": [0], + "i": { "x": [0.75], "y": [0.75] }, + "o": { "x": [0.25], "y": [0.25] } + }, + { + "t": 100, + "s": [180], + "i": { "x": [0.75], "y": [0.75] }, + "o": { "x": [0.25], "y": [0.25] } + } + ], + "ix": 2 + }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "gr", + "it": [ + { + "ty": "gr", + "nm": "Path", + "it": [ + { + "ty": "gr", + "nm": "Path", + "it": [ + { + "ty": "sh", + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "v": [ + [16.204001519577492, 10.809684651948828], + [107.78162573450534, 10.809684651948828], + [107.78162573450534, 103.16527938802837], + [16.204001519577492, 103.16527938802837], + [16.204001519577492, 10.809684651948828], + [16.204001519577492, 10.809684651948828] + ], + "i": [ + [0, 0], + [-30.52587473830928, 0], + [0, -30.785198245359847], + [30.52587473830929, 0], + [0, 30.785198245359847], + [0, 0] + ], + "o": [ + [30.52587473830928, 0], + [0, 30.785198245359833], + [-30.52587473830928, 0], + [0, -30.785198245359847], + [0, 0], + [0, 0] + ] + } + } + }, + { + "ty": "tr", + "p": { "a": 0, "k": [-33.81110763549805, -68.36682891845703], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "gr", + "nm": "Path", + "it": [ + { + "ty": "sh", + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "v": [ + [5.187128475098463, 75.41686707022457], + [0.11734912644122932, 75.41686707022457], + [0.11734912644122932, 85.55642576753904], + [5.187128475098463, 85.55642576753904], + [5.187128475098463, 101.79962447177776], + [10.253194243805027, 106.869403820435], + [106.98749566286637, 106.869403820435], + [112.05356143157292, 101.79962447177776], + [112.05356143157292, 5.066065768706561], + [106.98749566286637, 0], + [10.2569078237557, 0], + [5.190842055049137, 5.066065768706561], + [5.190842055049137, 21.277327685369535], + [0, 21.277327685369535], + [0, 31.413172802733335], + [5.190842055049137, 31.413172802733335], + [5.190842055049137, 75.4176097862147], + [5.187128475098463, 75.4176097862147], + [5.187128475098463, 75.41686707022457] + ], + "i": [ + [0, 0], + [1.6899264495524133, 0], + [0, -3.3798528991048267], + [-1.6899264495524133, 0], + [0, -5.414399568079578], + [-2.795582986865779, 0], + [-32.24476713968711, 0], + [0, 2.799296566816458], + [0, 32.244519567690396], + [2.7955829868657895, 0], + [32.243529279703566, 0], + [0, -2.800039282806585], + [0, -5.40375397222099], + [1.7302806850163799, 0], + [0, -3.3786150391212706], + [-1.7302806850163799, 0], + [0, -14.66814566116046], + [0.0012378599835561636, 0], + [0, 0.0002475719967196764] + ], + "o": [ + [-1.6899264495524133, 0], + [0, 3.3798528991048267], + [1.6899264495524107, 0], + [0, 5.414399568079578], + [0, 2.799296566816458], + [32.24476713968713, 0], + [2.799296566816458, 0], + [0, -32.2445195676904], + [0, -2.799296566816452], + [-32.24352927970354, 0], + [-2.799296566816452, 0], + [0, 5.40375397222099], + [-1.7302806850163799, 0], + [0, 3.3786150391212706], + [1.7302806850163799, 0], + [0, 14.66814566116045], + [-0.0012378599835561636, 0], + [0, -0.0002475719967196764], + [0, 0] + ] + } + } + }, + { + "ty": "tr", + "p": { "a": 0, "k": [-30.517711639404297, -64.98767852783203], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "fl", + "c": { "a": 0, "k": [0.00392156862745098, 0, 0.00784313725490196], "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "r": 1, + "bm": 0, + "hd": true + }, + { + "ty": "tr", + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "tr", + "p": { "a": 0, "k": [738.9981689453125, 819], "ix": 2 }, + "a": { "a": 0, "k": [75.43499755859375, 71.94499969482422], "ix": 2 }, + "s": { "a": 0, "k": [366.3825750350952, 366.3825750350952], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "gr", + "it": [ + { + "ty": "gr", + "nm": "Path", + "it": [ + { + "ty": "fl", + "c": { "a": 0, "k": [0.00392156862745098, 0, 0.00784313725490196], "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "r": 1, + "bm": 0, + "hd": true + }, + { + "ty": "tr", + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [100, 100], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "tr", + "p": { "a": 0, "k": [360.7227783203125, 392.2637023925781], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 2 }, + "s": { "a": 0, "k": [454.6020030975342, 454.6020030975342], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + }, + { + "ty": "tr", + "p": { "a": 0, "k": [80.75597381591797, 122.56097412109375], "ix": 2 }, + "a": { "a": 0, "k": [399.99816274642944, 399.99998253828016], "ix": 2 }, + "s": { "a": 0, "k": [112.70011646566, 112.70011646566], "ix": 2 }, + "r": { "a": 0, "k": 0, "ix": 2 }, + "o": { "a": 0, "k": 100, "ix": 2 }, + "sk": { "a": 0, "k": 0, "ix": 2 }, + "sa": { "a": 0, "k": 0, "ix": 2 } + } + ] + } + ], + "ip": 0, + "op": 201, + "st": 0, + "bm": 0 + } + ], + "markers": [] +} + diff --git a/frontend/src/components/auth/TeamInviteStep.tsx b/frontend/src/components/auth/TeamInviteStep.tsx index 5a9f11bde..b0a5618d9 100644 --- a/frontend/src/components/auth/TeamInviteStep.tsx +++ b/frontend/src/components/auth/TeamInviteStep.tsx @@ -4,7 +4,6 @@ import { useNavigate } from "@tanstack/react-router"; import { useAddUsersToOrg } from "@app/hooks/api"; import { useFetchServerStatus } from "@app/hooks/api/serverDetails"; -import { ProjectType } from "@app/hooks/api/workspace/types"; import { usePopUp } from "@app/hooks/usePopUp"; import { Button, EmailServiceSetupModal } from "../v2"; @@ -23,7 +22,7 @@ export default function TeamInviteStep(): JSX.Element { // Redirect user to the getting started page const redirectToHome = async () => { - navigate({ to: `/organization/${ProjectType.SecretManager}/overview` as const }); + navigate({ to: "/organization/projects" as const }); }; const inviteUsers = async ({ emails: inviteEmails }: { emails: string }) => { diff --git a/frontend/src/components/navigation/NavHeader.tsx b/frontend/src/components/navigation/NavHeader.tsx index 4386818dd..ba25fd847 100644 --- a/frontend/src/components/navigation/NavHeader.tsx +++ b/frontend/src/components/navigation/NavHeader.tsx @@ -71,7 +71,7 @@ export default function NavHeader({ {currentOrg?.name?.charAt(0)} {currentOrg?.name} @@ -93,7 +93,7 @@ export default function NavHeader({ {pageName === "Secrets" ? ( @@ -129,7 +129,7 @@ export default function NavHeader({
@@ -191,7 +191,7 @@ export default function NavHeader({
) : ( ) : ( , { position: "bottom-right", ...toastProps, + autoClose: toastProps.autoClose || 15000, theme: "dark", type: myProps?.type || "info" }); diff --git a/frontend/src/components/organization/CreateOrgModal/CreateOrgModal.tsx b/frontend/src/components/organization/CreateOrgModal/CreateOrgModal.tsx index bdf1c5c81..23c3fe6a5 100644 --- a/frontend/src/components/organization/CreateOrgModal/CreateOrgModal.tsx +++ b/frontend/src/components/organization/CreateOrgModal/CreateOrgModal.tsx @@ -7,7 +7,6 @@ import z from "zod"; import { createNotification } from "@app/components/notifications"; import { Button, FormControl, Input, Modal, ModalContent } from "@app/components/v2"; import { useCreateOrg, useSelectOrganization } from "@app/hooks/api"; -import { ProjectType } from "@app/hooks/api/workspace/types"; import { GenericResourceNameSchema } from "@app/lib/schemas"; const schema = z @@ -59,10 +58,7 @@ export const CreateOrgModal: FC = ({ isOpen, onClose }) => }); navigate({ - to: `/organization/${ProjectType.SecretManager}/overview` as const, - params: { - organizationId: organization.id - } + to: "/organization/projects" }); localStorage.setItem("orgData.id", organization.id); diff --git a/frontend/src/components/project/ProjectOverviewChangeSection.tsx b/frontend/src/components/project/ProjectOverviewChangeSection.tsx index 0f88ec2e9..85a4ae46f 100644 --- a/frontend/src/components/project/ProjectOverviewChangeSection.tsx +++ b/frontend/src/components/project/ProjectOverviewChangeSection.tsx @@ -5,12 +5,14 @@ import { z } from "zod"; import { createNotification } from "@app/components/notifications"; import { ProjectPermissionCan } from "@app/components/permissions"; -import { Button, FormControl, Input, TextArea } from "@app/components/v2"; +import { Button, FormControl, Input, Select, SelectItem, TextArea } from "@app/components/v2"; import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context"; import { useUpdateProject } from "@app/hooks/api"; +import { ProjectType } from "@app/hooks/api/workspace/types"; const baseFormSchema = z.object({ name: z.string().min(1, "Required").max(64, "Too long, maximum length is 64 characters"), + defaultProduct: z.nativeEnum(ProjectType).default(ProjectType.SecretManager), description: z .string() .trim() @@ -50,6 +52,7 @@ export const ProjectOverviewChangeSection = ({ showSlugField = false }: Props) = reset({ name: currentWorkspace.name, description: currentWorkspace.description ?? "", + defaultProduct: currentWorkspace.defaultProduct, ...(showSlugField && { slug: currentWorkspace.slug }) }); } @@ -63,6 +66,7 @@ export const ProjectOverviewChangeSection = ({ showSlugField = false }: Props) = projectID: currentWorkspace.id, newProjectName: data.name, newProjectDescription: data.description, + defaultProduct: data.defaultProduct, ...(showSlugField && "slug" in data && { newSlug: data.slug !== currentWorkspace.slug ? data.slug : undefined @@ -212,6 +216,31 @@ export const ProjectOverviewChangeSection = ({ showSlugField = false }: Props) = + ( + + + + )} + />
; interface NewProjectModalProps { isOpen: boolean; onOpenChange: (isOpen: boolean) => void; - projectType: ProjectType; } -type NewProjectFormProps = Pick; +type NewProjectFormProps = Pick; -const NewProjectForm = ({ onOpenChange, projectType }: NewProjectFormProps) => { +const NewProjectForm = ({ onOpenChange }: NewProjectFormProps) => { const navigate = useNavigate(); const { currentOrg } = useOrganization(); const { permission } = useOrgPermission(); @@ -72,7 +70,7 @@ const NewProjectForm = ({ onOpenChange, projectType }: NewProjectFormProps) => { OrgPermissionSubjects.ProjectTemplates ); - const { data: projectTemplates = [] } = useListProjectTemplates(projectType, { + const { data: projectTemplates = [] } = useListProjectTemplates({ enabled: Boolean(canReadProjectTemplates && subscription?.projectTemplates) }); @@ -115,15 +113,17 @@ const NewProjectForm = ({ onOpenChange, projectType }: NewProjectFormProps) => { projectName: name, projectDescription: description, kmsKeyId: kmsKeyId !== INTERNAL_KMS_KEY_ID ? kmsKeyId : undefined, - template, - type: projectType + template }); await refetchWorkspaces(); createNotification({ text: "Project created", type: "success" }); reset(); onOpenChange(false); - navigate({ to: getProjectHomePage(project), params: { projectId: project.id } }); + navigate({ + to: getProjectHomePage(project.defaultProduct), + params: { projectId: project.id } + }); } catch (err) { console.error(err); createNotification({ text: "Failed to create project", type: "error" }); @@ -270,18 +270,14 @@ const NewProjectForm = ({ onOpenChange, projectType }: NewProjectFormProps) => { ); }; -export const NewProjectModal: FC = ({ - isOpen, - onOpenChange, - projectType -}) => { +export const NewProjectModal: FC = ({ isOpen, onOpenChange }) => { return ( - + ); diff --git a/frontend/src/components/projects/ProjectSettings/ProjectSettings.tsx b/frontend/src/components/projects/ProjectSettings/ProjectSettings.tsx deleted file mode 100644 index 3919ad86c..000000000 --- a/frontend/src/components/projects/ProjectSettings/ProjectSettings.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { useState } from "react"; - -import { Tab, TabList, TabPanel, Tabs } from "@app/components/v2"; - -import { ProjectTemplatesTab } from "./components"; - -const tabs = [ - { name: "Project Templates", key: "project-templates", component: ProjectTemplatesTab } -]; - -export const ProjectSettings = () => { - const [selectedTab, setSelectedTab] = useState(tabs[0].key); - - return ( - - - {tabs.map((tab) => ( - - {tab.name} - - ))} - - {tabs.map(({ key, component: Component }) => ( - - - - ))} - - ); -}; diff --git a/frontend/src/components/projects/ProjectSettings/components/index.tsx b/frontend/src/components/projects/ProjectSettings/components/index.tsx deleted file mode 100644 index 35ad65ee0..000000000 --- a/frontend/src/components/projects/ProjectSettings/components/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export * from "./ProjectTemplatesTab"; diff --git a/frontend/src/components/projects/ProjectSettings/index.tsx b/frontend/src/components/projects/ProjectSettings/index.tsx deleted file mode 100644 index e2f0626f6..000000000 --- a/frontend/src/components/projects/ProjectSettings/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export * from "./ProjectSettings"; diff --git a/frontend/src/components/secret-syncs/forms/SecretSyncDestinationFields/SecretSyncDestinationFields.tsx b/frontend/src/components/secret-syncs/forms/SecretSyncDestinationFields/SecretSyncDestinationFields.tsx index b6074d270..da8686cc1 100644 --- a/frontend/src/components/secret-syncs/forms/SecretSyncDestinationFields/SecretSyncDestinationFields.tsx +++ b/frontend/src/components/secret-syncs/forms/SecretSyncDestinationFields/SecretSyncDestinationFields.tsx @@ -25,6 +25,7 @@ import { TeamCitySyncFields } from "./TeamCitySyncFields"; import { TerraformCloudSyncFields } from "./TerraformCloudSyncFields"; import { VercelSyncFields } from "./VercelSyncFields"; import { WindmillSyncFields } from "./WindmillSyncFields"; +import { ZabbixSyncFields } from "./ZabbixSyncFields"; export const SecretSyncDestinationFields = () => { const { watch } = useFormContext(); @@ -76,6 +77,8 @@ export const SecretSyncDestinationFields = () => { return ; case SecretSync.CloudflarePages: return ; + case SecretSync.Zabbix: + return ; default: throw new Error(`Unhandled Destination Config Field: ${destination}`); } diff --git a/frontend/src/components/secret-syncs/forms/SecretSyncDestinationFields/ZabbixSyncFields.tsx b/frontend/src/components/secret-syncs/forms/SecretSyncDestinationFields/ZabbixSyncFields.tsx new file mode 100644 index 000000000..e00100284 --- /dev/null +++ b/frontend/src/components/secret-syncs/forms/SecretSyncDestinationFields/ZabbixSyncFields.tsx @@ -0,0 +1,147 @@ +import { Controller, useFormContext, useWatch } from "react-hook-form"; +import { SingleValue } from "react-select"; + +import { SecretSyncConnectionField } from "@app/components/secret-syncs/forms/SecretSyncConnectionField"; +import { FilterableSelect, FormControl, Select, SelectItem } from "@app/components/v2"; +import { + TZabbixHost, + useZabbixConnectionListHosts, + ZABBIX_SYNC_SCOPES, + ZabbixMacroType, + ZabbixSyncScope +} from "@app/hooks/api/appConnections/zabbix"; +import { SecretSync } from "@app/hooks/api/secretSyncs"; + +import { TSecretSyncForm } from "../schemas"; + +export const ZabbixSyncFields = () => { + const { control, watch, setValue } = useFormContext< + TSecretSyncForm & { destination: SecretSync.Zabbix } + >(); + + const connectionId = useWatch({ name: "connection.id", control }); + const currentScope = watch("destinationConfig.scope"); + + const { data: hosts = [], isPending: isHostsPending } = useZabbixConnectionListHosts( + connectionId, + { + enabled: Boolean(connectionId) + } + ); + + return ( + <> + { + setValue("destinationConfig.scope", ZabbixSyncScope.Global); + setValue("destinationConfig.hostId", ""); + setValue("destinationConfig.hostName", ""); + }} + /> + ( + +

+ Specify how Infisical should manage secrets from Zabbix. The following options are + available: +

+
    + {Object.values(ZABBIX_SYNC_SCOPES).map(({ name, description }) => { + return ( +
  • +

    + {name}: {description} +

    +
  • + ); + })} +
+
+ } + > + + + )} + /> + {currentScope === ZabbixSyncScope.Host && ( + ( + + host.hostId === value) ?? null} + onChange={(option) => { + const selectedOption = option as SingleValue; + onChange(selectedOption?.hostId ?? null); + + if (selectedOption) { + setValue("destinationConfig.hostName", selectedOption.host); + } else { + setValue("destinationConfig.hostName", ""); + } + }} + options={hosts} + placeholder="Select a host..." + getOptionLabel={(option) => option.host} + getOptionValue={(option) => option.hostId} + /> + + )} + /> + )} + ( + + + + )} + /> + + ); +}; diff --git a/frontend/src/components/secret-syncs/forms/SecretSyncOptionsFields/SecretSyncOptionsFields.tsx b/frontend/src/components/secret-syncs/forms/SecretSyncOptionsFields/SecretSyncOptionsFields.tsx index a61c2a6b8..d91fc3771 100644 --- a/frontend/src/components/secret-syncs/forms/SecretSyncOptionsFields/SecretSyncOptionsFields.tsx +++ b/frontend/src/components/secret-syncs/forms/SecretSyncOptionsFields/SecretSyncOptionsFields.tsx @@ -23,6 +23,7 @@ export const SecretSyncOptionsFields = ({ hideInitialSync }: Props) => { const { control, watch } = useFormContext(); const destination = watch("destination"); + const currentSyncOption = watch("syncOptions"); const destinationName = SECRET_SYNC_MAP[destination].name; @@ -57,6 +58,7 @@ export const SecretSyncOptionsFields = ({ hideInitialSync }: Props) => { case SecretSync.Flyio: case SecretSync.GitLab: case SecretSync.CloudflarePages: + case SecretSync.Zabbix: AdditionalSyncOptionsFieldsComponent = null; break; default: @@ -127,8 +129,9 @@ export const SecretSyncOptionsFields = ({ hideInitialSync }: Props) => { {!syncOption?.canImportSecrets && (

- {destinationName} only supports overwriting destination secrets. Secrets not present - in Infisical will be removed from the destination. + {destinationName} only supports overwriting destination secrets.{" "} + {!currentSyncOption.disableSecretDeletion && + "Secrets not present in Infisical will be removed from the destination."}

)} diff --git a/frontend/src/components/secret-syncs/forms/SecretSyncReviewFields/SecretSyncReviewFields.tsx b/frontend/src/components/secret-syncs/forms/SecretSyncReviewFields/SecretSyncReviewFields.tsx index fb639e91b..e2ffb9fa6 100644 --- a/frontend/src/components/secret-syncs/forms/SecretSyncReviewFields/SecretSyncReviewFields.tsx +++ b/frontend/src/components/secret-syncs/forms/SecretSyncReviewFields/SecretSyncReviewFields.tsx @@ -35,6 +35,7 @@ import { TeamCitySyncReviewFields } from "./TeamCitySyncReviewFields"; import { TerraformCloudSyncReviewFields } from "./TerraformCloudSyncReviewFields"; import { VercelSyncReviewFields } from "./VercelSyncReviewFields"; import { WindmillSyncReviewFields } from "./WindmillSyncReviewFields"; +import { ZabbixSyncReviewFields } from "./ZabbixSyncReviewFields"; export const SecretSyncReviewFields = () => { const { watch } = useFormContext(); @@ -124,6 +125,9 @@ export const SecretSyncReviewFields = () => { case SecretSync.CloudflarePages: DestinationFieldsComponent = ; break; + case SecretSync.Zabbix: + DestinationFieldsComponent = ; + break; default: throw new Error(`Unhandled Destination Review Fields: ${destination}`); } diff --git a/frontend/src/components/secret-syncs/forms/SecretSyncReviewFields/ZabbixSyncReviewFields.tsx b/frontend/src/components/secret-syncs/forms/SecretSyncReviewFields/ZabbixSyncReviewFields.tsx new file mode 100644 index 000000000..c58e35382 --- /dev/null +++ b/frontend/src/components/secret-syncs/forms/SecretSyncReviewFields/ZabbixSyncReviewFields.tsx @@ -0,0 +1,31 @@ +import { useFormContext } from "react-hook-form"; + +import { TSecretSyncForm } from "@app/components/secret-syncs/forms/schemas"; +import { GenericFieldLabel } from "@app/components/v2"; +import { ZabbixSyncScope } from "@app/hooks/api/appConnections/zabbix"; +import { SecretSync } from "@app/hooks/api/secretSyncs"; + +const isTextMacro = (macroType: number) => macroType === 0; + +export const ZabbixSyncReviewFields = () => { + const { watch } = useFormContext(); + const scope = watch("destinationConfig.scope"); + const hostId = watch("destinationConfig.hostId"); + const hostName = watch("destinationConfig.hostName"); + const macroType = watch("destinationConfig.macroType"); + + return ( + <> + {scope} + {scope === ZabbixSyncScope.Host && ( + <> + {hostId} + {hostName} + + )} + + {isTextMacro(macroType) ? "Text" : "Secret"} + + + ); +}; diff --git a/frontend/src/components/secret-syncs/forms/schemas/secret-sync-schema.ts b/frontend/src/components/secret-syncs/forms/schemas/secret-sync-schema.ts index 5d15492eb..331768e7f 100644 --- a/frontend/src/components/secret-syncs/forms/schemas/secret-sync-schema.ts +++ b/frontend/src/components/secret-syncs/forms/schemas/secret-sync-schema.ts @@ -22,6 +22,7 @@ import { TeamCitySyncDestinationSchema } from "./teamcity-sync-destination-schem import { TerraformCloudSyncDestinationSchema } from "./terraform-cloud-destination-schema"; import { VercelSyncDestinationSchema } from "./vercel-sync-destination-schema"; import { WindmillSyncDestinationSchema } from "./windmill-sync-destination-schema"; +import { ZabbixSyncDestinationSchema } from "./zabbix-sync-destination-schema"; const SecretSyncUnionSchema = z.discriminatedUnion("destination", [ AwsParameterStoreSyncDestinationSchema, @@ -45,7 +46,8 @@ const SecretSyncUnionSchema = z.discriminatedUnion("destination", [ RenderSyncDestinationSchema, FlyioSyncDestinationSchema, GitlabSyncDestinationSchema, - CloudflarePagesSyncDestinationSchema + CloudflarePagesSyncDestinationSchema, + ZabbixSyncDestinationSchema ]); export const SecretSyncFormSchema = SecretSyncUnionSchema; diff --git a/frontend/src/components/secret-syncs/forms/schemas/zabbix-sync-destination-schema.ts b/frontend/src/components/secret-syncs/forms/schemas/zabbix-sync-destination-schema.ts new file mode 100644 index 000000000..694c6c7af --- /dev/null +++ b/frontend/src/components/secret-syncs/forms/schemas/zabbix-sync-destination-schema.ts @@ -0,0 +1,27 @@ +import { z } from "zod"; + +import { BaseSecretSyncSchema } from "@app/components/secret-syncs/forms/schemas/base-secret-sync-schema"; +import { ZabbixMacroType, ZabbixSyncScope } from "@app/hooks/api/appConnections/zabbix"; +import { SecretSync } from "@app/hooks/api/secretSyncs"; + +export const ZabbixSyncDestinationSchema = BaseSecretSyncSchema().merge( + z.object({ + destination: z.literal(SecretSync.Zabbix), + destinationConfig: z.discriminatedUnion("scope", [ + z.object({ + scope: z.literal(ZabbixSyncScope.Host), + hostId: z.string().trim().min(1, "Host ID required"), + hostName: z.string().trim().min(1, "Host name required"), + macroType: z.nativeEnum(ZabbixMacroType, { + errorMap: () => ({ message: "Macro type must be either 'text' or 'secret'" }) + }) + }), + z.object({ + scope: z.literal(ZabbixSyncScope.Global), + macroType: z.nativeEnum(ZabbixMacroType, { + errorMap: () => ({ message: "Macro type must be either 'text' or 'secret'" }) + }) + }) + ]) + }) +); diff --git a/frontend/src/components/utilities/ShouldWrapComponent.tsx b/frontend/src/components/utilities/ShouldWrapComponent.tsx new file mode 100644 index 000000000..25f5d1659 --- /dev/null +++ b/frontend/src/components/utilities/ShouldWrapComponent.tsx @@ -0,0 +1,20 @@ +import { ComponentType, ReactNode } from "react"; + +type ShouldWrapProps> = { + children: ReactNode; + wrapper: ComponentType; + isWrapped?: boolean; +} & T; + +export const ShouldWrap = >({ + children, + wrapper: Wrapper, + isWrapped = false, + ...wrapperProps +}: ShouldWrapProps) => { + if (isWrapped) { + return {children}; + } + + return children; +}; diff --git a/frontend/src/components/v2/Breadcrumb/Breadcrumb.tsx b/frontend/src/components/v2/Breadcrumb/Breadcrumb.tsx index 0afd1bb1f..0b97a58c4 100644 --- a/frontend/src/components/v2/Breadcrumb/Breadcrumb.tsx +++ b/frontend/src/components/v2/Breadcrumb/Breadcrumb.tsx @@ -1,6 +1,6 @@ /* eslint-disable react/prop-types */ import React from "react"; -import { faCaretDown, faChevronRight, faEllipsis } from "@fortawesome/free-solid-svg-icons"; +import { faEllipsis, faSort } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Link, ReactNode } from "@tanstack/react-router"; import { LinkComponentProps } from "node_modules/@tanstack/react-router/dist/esm/link"; @@ -40,7 +40,7 @@ const BreadcrumbItem = React.forwardRef (
  • ) @@ -78,13 +78,8 @@ const BreadcrumbPage = React.forwardRef) => ( -
  • ); BreadcrumbSeparator.displayName = "BreadcrumbSeparator"; @@ -139,12 +134,12 @@ const BreadcrumbContainer = ({ breadcrumbs }: { breadcrumbs: TBreadcrumbFormat[] - - {el.label} + + {el.label} - + {el?.dropdownTitle && {el.dropdownTitle}} {el.links.map((i, dropIndex) => ( ( {...props} > {isLoading && ( - loading animation )} {leftIcon && ( diff --git a/frontend/src/components/v2/ContentLoader/ContentLoader.tsx b/frontend/src/components/v2/ContentLoader/ContentLoader.tsx index 91937ff63..f60668aec 100644 --- a/frontend/src/components/v2/ContentLoader/ContentLoader.tsx +++ b/frontend/src/components/v2/ContentLoader/ContentLoader.tsx @@ -5,6 +5,8 @@ import { useEffect, useState } from "react"; import { AnimatePresence, motion } from "framer-motion"; import { twMerge } from "tailwind-merge"; +import { Lottie } from "../Lottie"; + type Props = { text?: string | string[]; frequency?: number; @@ -31,14 +33,7 @@ export const ContentLoader = ({ text, frequency = 2000, className }: Props) => { className )} > - loading animation + {text && isTextArray && ( { const [inputData, setInputData] = useState(""); @@ -70,7 +72,7 @@ export const DeleteActionModal = ({ - - ); - })} -
    - } - onClick={logOutUser} - > - Log Out - - - -
    -
    - {currentOrg.secretsProductEnabled && ( - - {({ isActive }) => ( - - Secrets - - )} - - )} - {currentOrg.pkiProductEnabled && ( - - {({ isActive }) => ( - - PKI - - )} - - )} - {currentOrg.kmsProductEnabled && ( - - {({ isActive }) => ( - - KMS - - )} - - )} - {currentOrg.sshProductEnabled && ( - - {({ isActive }) => ( - - SSH - - )} - - )} - {currentOrg.scannerProductEnabled && ( - - {({ isActive }) => ( - - Scanner - - )} - - )} - {(currentOrg.scannerProductEnabled || currentOrg.shareSecretsProductEnabled) && ( -
    - )} - {currentOrg.shareSecretsProductEnabled && ( - - {({ isActive }) => ( - - Share - - )} - - )} -
    - - setOpen(true)} - onMouseLeave={() => setOpen(false)} - asChild - > -
    - - Admin - -
    -
    - setOpen(true)} - onMouseLeave={() => setOpen(false)} - align="start" - side="right" - className="p-1" - > - Organization Options - - }> - Access Control - - - - }> - App Connections - - - - } - > - Gateways - - - - }> - Usage & Billing - - - - }> - Audit Logs - - - - } - > - SSO Settings - - - - }> - Organization Settings - - - Admin Panels - {user?.superAdmin && ( - - }> - Server Admin Console - - - )} - - }> - Organization Admin Console - - - -
    -
    -
    -
    - - setOpenSupport(true)} - onMouseLeave={() => setOpenSupport(false)} - className="w-full" - > - - - Support - - - setOpenSupport(true)} - onMouseLeave={() => setOpenSupport(false)} - align="end" - side="right" - className="p-1" - > - {INFISICAL_SUPPORT_OPTIONS.map(([icon, text, url]) => { - if (url === "server-admins" && isInfisicalCloud()) { - return null; - } - return ( - - {url === "server-admins" ? ( - - ) : ( - -
    - {icon} -
    {text}
    -
    -
    - )} -
    - ); - })} - {envConfig.PLATFORM_VERSION && ( -
    - - Version: {envConfig.PLATFORM_VERSION} -
    - )} -
    -
    - {subscription && subscription.slug === "starter" && !subscription.has_used_trial && ( - - - - )} - - setOpenUser(true)} - onMouseLeave={() => setOpenUser(false)} - className="w-full" - asChild - > -
    - User -
    -
    - setOpenUser(true)} - onMouseLeave={() => setOpenUser(false)} - side="right" - align="end" - className="p-1" - > -
    -
    -
    - -
    -
    -
    - {user?.firstName} {user?.lastName} -
    -
    {user.email}
    -
    -
    -
    - - Personal Settings - - - - Documentation - - - - - - Join Slack Community - - - -
    - - Copy Token - - - - -
    - }> - Log Out - - - -
    - - - - -
    - -
    -
    -
    - handlePopUpToggle("createOrg", false)} - /> - - ); -}; diff --git a/frontend/src/layouts/OrganizationLayout/components/MinimizedOrgSidebar/index.tsx b/frontend/src/layouts/OrganizationLayout/components/MinimizedOrgSidebar/index.tsx deleted file mode 100644 index f8df49034..000000000 --- a/frontend/src/layouts/OrganizationLayout/components/MinimizedOrgSidebar/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { MinimizedOrgSidebar } from "./MinimizedOrgSidebar"; diff --git a/frontend/src/layouts/OrganizationLayout/components/NavBar/Navbar.tsx b/frontend/src/layouts/OrganizationLayout/components/NavBar/Navbar.tsx new file mode 100644 index 000000000..4e9184789 --- /dev/null +++ b/frontend/src/layouts/OrganizationLayout/components/NavBar/Navbar.tsx @@ -0,0 +1,378 @@ +import { useState } from "react"; +import { faGithub, faSlack } from "@fortawesome/free-brands-svg-icons"; +import { faCircleQuestion, faUserCircle } from "@fortawesome/free-regular-svg-icons"; +import { + faArrowUpRightFromSquare, + faBook, + faCheck, + faCubes, + faEnvelope, + faInfo, + faInfoCircle, + faSignOut, + faSort, + faUser, + faUsers +} from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { useQueryClient } from "@tanstack/react-query"; +import { Link, useNavigate, useRouter, useRouterState } from "@tanstack/react-router"; + +import { Mfa } from "@app/components/auth/Mfa"; +import { createNotification } from "@app/components/notifications"; +import SecurityClient from "@app/components/utilities/SecurityClient"; +import { + BreadcrumbContainer, + Button, + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, + IconButton, + Modal, + ModalContent, + TBreadcrumbFormat, + Tooltip +} from "@app/components/v2"; +import { envConfig } from "@app/config/env"; +import { useOrganization, useSubscription, useUser } from "@app/context"; +import { isInfisicalCloud } from "@app/helpers/platform"; +import { useToggle } from "@app/hooks"; +import { useGetOrganizations, useLogoutUser, workspaceKeys } from "@app/hooks/api"; +import { authKeys, selectOrganization } from "@app/hooks/api/auth/queries"; +import { MfaMethod } from "@app/hooks/api/auth/types"; +import { getAuthToken } from "@app/hooks/api/reactQuery"; +import { SubscriptionPlan } from "@app/hooks/api/types"; +import { AuthMethod } from "@app/hooks/api/users/types"; +import { navigateUserToOrg } from "@app/pages/auth/LoginPage/Login.utils"; + +import { ServerAdminsPanel } from "../ServerAdminsPanel/ServerAdminsPanel"; + +const getPlan = (subscription: SubscriptionPlan) => { + if (subscription.groups) return "Enterprise"; + if (subscription.pitRecovery) return "Pro"; + return "Free"; +}; + +export const INFISICAL_SUPPORT_OPTIONS = [ + [ + , + "Support Forum", + "https://infisical.com/slack" + ], + [ + , + "Read Docs", + "https://infisical.com/docs/documentation/getting-started/introduction" + ], + [ + , + "GitHub Issues", + "https://github.com/Infisical/infisical/issues" + ], + [ + , + "Email Support", + "mailto:support@infisical.com" + ], + [ + , + "Instance Admins", + "server-admins" + ] +]; + +export const Navbar = () => { + const { user } = useUser(); + const { subscription } = useSubscription(); + const { currentOrg } = useOrganization(); + const [showAdminsModal, setShowAdminsModal] = useState(false); + + const { data: orgs } = useGetOrganizations(); + const navigate = useNavigate(); + const [requiredMfaMethod, setRequiredMfaMethod] = useState(MfaMethod.EMAIL); + const [mfaSuccessCallback, setMfaSuccessCallback] = useState<() => void>(() => {}); + const [shouldShowMfa, toggleShowMfa] = useToggle(false); + const router = useRouter(); + const queryClient = useQueryClient(); + + const matches = useRouterState({ select: (s) => s.matches.at(-1)?.context }); + const breadcrumbs = matches && "breadcrumbs" in matches ? matches.breadcrumbs : undefined; + + const handleOrgChange = async (orgId: string) => { + queryClient.removeQueries({ queryKey: authKeys.getAuthToken }); + queryClient.removeQueries({ queryKey: workspaceKeys.getAllUserWorkspace() }); + + const { token, isMfaEnabled, mfaMethod } = await selectOrganization({ + organizationId: orgId + }); + + if (isMfaEnabled) { + SecurityClient.setMfaToken(token); + if (mfaMethod) { + setRequiredMfaMethod(mfaMethod); + } + toggleShowMfa.on(); + setMfaSuccessCallback(() => () => handleOrgChange(orgId)); + return; + } + await router.invalidate(); + await navigateUserToOrg(navigate, orgId); + }; + + const logout = useLogoutUser(); + const logOutUser = async () => { + try { + console.log("Logging out..."); + await logout.mutateAsync(); + navigate({ to: "/login" }); + } catch (error) { + console.error(error); + } + }; + + const handleCopyToken = async () => { + try { + await window.navigator.clipboard.writeText(getAuthToken()); + createNotification({ + type: "success", + text: "Copied current login session token to clipboard" + }); + } catch (error) { + console.log(error); + createNotification({ type: "error", text: "Failed to copy user token to clipboard" }); + } + }; + + if (shouldShowMfa) { + return ( +
    + toggleShowMfa.off()} + /> +
    + ); + } + + return ( +
    +
    + + infisical logo + +
    +
    +

    /

    + + +
    +
    + +
    +
    {currentOrg?.name}
    +
    + {getPlan(subscription)} +
    +
    + + +
    + + + +
    +
    + +
    organizations
    + {orgs?.map((org) => { + return ( + + + + ); + })} +
    + } onClick={logOutUser}> + Log Out + + + +

    /

    +
    +
    + {breadcrumbs ? ( + + ) : null} +
    +
    + + +
    + +
    +
    + + {INFISICAL_SUPPORT_OPTIONS.map(([icon, text, url]) => { + if (url === "server-admins" && isInfisicalCloud()) { + return null; + } + return ( + + {url === "server-admins" ? ( + + ) : ( + +
    + {icon} +
    {text}
    +
    +
    + )} +
    + ); + })} + {envConfig.PLATFORM_VERSION && ( +
    + + Version: {envConfig.PLATFORM_VERSION} +
    + )} +
    +
    + + +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    + {user?.firstName} {user?.lastName} +
    +
    {user.email}
    +
    +
    +
    + + Personal Settings + + + + Documentation + + + + + + Join Slack Community + + + +
    + + Copy Token + + + + +
    + }> + Log Out + + + + + +
    + +
    +
    +
    +
    + ); +}; diff --git a/frontend/src/layouts/OrganizationLayout/components/NavBar/index.tsx b/frontend/src/layouts/OrganizationLayout/components/NavBar/index.tsx new file mode 100644 index 000000000..d97ce393e --- /dev/null +++ b/frontend/src/layouts/OrganizationLayout/components/NavBar/index.tsx @@ -0,0 +1 @@ +export { Navbar } from "./Navbar"; diff --git a/frontend/src/layouts/OrganizationLayout/components/OrgSidebar/OrgSidebar.tsx b/frontend/src/layouts/OrganizationLayout/components/OrgSidebar/OrgSidebar.tsx new file mode 100644 index 000000000..89da0e2de --- /dev/null +++ b/frontend/src/layouts/OrganizationLayout/components/OrgSidebar/OrgSidebar.tsx @@ -0,0 +1,237 @@ +import { + faBook, + faCheckCircle, + faCog, + faCubes, + faDoorClosed, + faInfinity, + faMoneyBill, + faPlug, + faShare, + faUserCog, + faUsers, + faUserTie +} from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { Link } from "@tanstack/react-router"; +import { AnimatePresence, motion } from "framer-motion"; + +import { CreateOrgModal } from "@app/components/organization/CreateOrgModal"; +import { Menu, MenuGroup, MenuItem, Tooltip } from "@app/components/v2"; +import { useOrganization, useSubscription, useUser } from "@app/context"; +import { usePopUp } from "@app/hooks"; +import { useGetOrgTrialUrl } from "@app/hooks/api"; + +type Props = { + isHidden?: boolean; +}; + +export const OrgSidebar = ({ isHidden }: Props) => { + const { subscription } = useSubscription(); + + const { user } = useUser(); + const { mutateAsync } = useGetOrgTrialUrl(); + + const { currentOrg } = useOrganization(); + + const { popUp, handlePopUpToggle } = usePopUp(["createOrg"] as const); + + return ( + <> + + {!isHidden && ( + +
    + } + > + Organization Admin + + + {user.superAdmin && ( + + + +
    + } + > + Server Console + + + )} + + + + )} + + handlePopUpToggle("createOrg", false)} + /> + + ); +}; diff --git a/frontend/src/layouts/OrganizationLayout/components/OrgSidebar/index.tsx b/frontend/src/layouts/OrganizationLayout/components/OrgSidebar/index.tsx new file mode 100644 index 000000000..315d7ffab --- /dev/null +++ b/frontend/src/layouts/OrganizationLayout/components/OrgSidebar/index.tsx @@ -0,0 +1 @@ +export { OrgSidebar } from "./OrgSidebar"; diff --git a/frontend/src/layouts/PersonalSettingsLayout/PersonalSettingsLayout.tsx b/frontend/src/layouts/PersonalSettingsLayout/PersonalSettingsLayout.tsx index 5ae5edd37..c2835996a 100644 --- a/frontend/src/layouts/PersonalSettingsLayout/PersonalSettingsLayout.tsx +++ b/frontend/src/layouts/PersonalSettingsLayout/PersonalSettingsLayout.tsx @@ -11,10 +11,9 @@ import { DropdownMenuTrigger } from "@app/components/v2"; import { envConfig } from "@app/config/env"; -import { ProjectType } from "@app/hooks/api/workspace/types"; import { InsecureConnectionBanner } from "../OrganizationLayout/components/InsecureConnectionBanner"; -import { INFISICAL_SUPPORT_OPTIONS } from "../OrganizationLayout/components/MinimizedOrgSidebar/MinimizedOrgSidebar"; +import { INFISICAL_SUPPORT_OPTIONS } from "../OrganizationLayout/components/NavBar/Navbar"; export const PersonalSettingsLayout = () => { const { t } = useTranslation(); @@ -27,7 +26,7 @@ export const PersonalSettingsLayout = () => {