mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-11 19:28:58 +00:00
merge main
This commit is contained in:
@@ -15,8 +15,8 @@ import { mockSmtpServer } from "./mocks/smtp";
|
|||||||
import { initDbConnection } from "@app/db";
|
import { initDbConnection } from "@app/db";
|
||||||
import { queueServiceFactory } from "@app/queue";
|
import { queueServiceFactory } from "@app/queue";
|
||||||
import { keyStoreFactory } from "@app/keystore/keystore";
|
import { keyStoreFactory } from "@app/keystore/keystore";
|
||||||
import { Redis } from "ioredis";
|
|
||||||
import { initializeHsmModule } from "@app/ee/services/hsm/hsm-fns";
|
import { initializeHsmModule } from "@app/ee/services/hsm/hsm-fns";
|
||||||
|
import { buildRedisFromConfig } from "@app/lib/config/redis";
|
||||||
|
|
||||||
dotenv.config({ path: path.join(__dirname, "../../.env.test"), debug: true });
|
dotenv.config({ path: path.join(__dirname, "../../.env.test"), debug: true });
|
||||||
export default {
|
export default {
|
||||||
@@ -30,7 +30,7 @@ export default {
|
|||||||
dbRootCert: envConfig.DB_ROOT_CERT
|
dbRootCert: envConfig.DB_ROOT_CERT
|
||||||
});
|
});
|
||||||
|
|
||||||
const redis = new Redis(envConfig.REDIS_URL);
|
const redis = buildRedisFromConfig(envConfig);
|
||||||
await redis.flushdb("SYNC");
|
await redis.flushdb("SYNC");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -55,8 +55,8 @@ export default {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const smtp = mockSmtpServer();
|
const smtp = mockSmtpServer();
|
||||||
const queue = queueServiceFactory(envConfig.REDIS_URL, { dbConnectionUrl: envConfig.DB_CONNECTION_URI });
|
const queue = queueServiceFactory(envConfig, { dbConnectionUrl: envConfig.DB_CONNECTION_URI });
|
||||||
const keyStore = keyStoreFactory(envConfig.REDIS_URL);
|
const keyStore = keyStoreFactory(envConfig);
|
||||||
|
|
||||||
const hsmModule = initializeHsmModule(envConfig);
|
const hsmModule = initializeHsmModule(envConfig);
|
||||||
hsmModule.initialize();
|
hsmModule.initialize();
|
||||||
|
|||||||
Generated
+1877
File diff suppressed because it is too large
Load Diff
@@ -131,6 +131,7 @@
|
|||||||
"@aws-sdk/client-elasticache": "^3.637.0",
|
"@aws-sdk/client-elasticache": "^3.637.0",
|
||||||
"@aws-sdk/client-iam": "^3.525.0",
|
"@aws-sdk/client-iam": "^3.525.0",
|
||||||
"@aws-sdk/client-kms": "^3.609.0",
|
"@aws-sdk/client-kms": "^3.609.0",
|
||||||
|
"@aws-sdk/client-route-53": "^3.810.0",
|
||||||
"@aws-sdk/client-secrets-manager": "^3.504.0",
|
"@aws-sdk/client-secrets-manager": "^3.504.0",
|
||||||
"@aws-sdk/client-sts": "^3.600.0",
|
"@aws-sdk/client-sts": "^3.600.0",
|
||||||
"@casl/ability": "^6.5.0",
|
"@casl/ability": "^6.5.0",
|
||||||
@@ -174,6 +175,7 @@
|
|||||||
"@slack/oauth": "^3.0.2",
|
"@slack/oauth": "^3.0.2",
|
||||||
"@slack/web-api": "^7.8.0",
|
"@slack/web-api": "^7.8.0",
|
||||||
"@ucast/mongo2js": "^1.3.4",
|
"@ucast/mongo2js": "^1.3.4",
|
||||||
|
"acme-client": "^5.4.0",
|
||||||
"ajv": "^8.12.0",
|
"ajv": "^8.12.0",
|
||||||
"argon2": "^0.31.2",
|
"argon2": "^0.31.2",
|
||||||
"aws-sdk": "^2.1553.0",
|
"aws-sdk": "^2.1553.0",
|
||||||
|
|||||||
Vendored
+5
@@ -54,6 +54,7 @@ import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
|
|||||||
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
||||||
import { TCertificateServiceFactory } from "@app/services/certificate/certificate-service";
|
import { TCertificateServiceFactory } from "@app/services/certificate/certificate-service";
|
||||||
import { TCertificateAuthorityServiceFactory } from "@app/services/certificate-authority/certificate-authority-service";
|
import { TCertificateAuthorityServiceFactory } from "@app/services/certificate-authority/certificate-authority-service";
|
||||||
|
import { TInternalCertificateAuthorityServiceFactory } from "@app/services/certificate-authority/internal/internal-certificate-authority-service";
|
||||||
import { TCertificateTemplateServiceFactory } from "@app/services/certificate-template/certificate-template-service";
|
import { TCertificateTemplateServiceFactory } from "@app/services/certificate-template/certificate-template-service";
|
||||||
import { TCmekServiceFactory } from "@app/services/cmek/cmek-service";
|
import { TCmekServiceFactory } from "@app/services/cmek/cmek-service";
|
||||||
import { TExternalGroupOrgRoleMappingServiceFactory } from "@app/services/external-group-org-role-mapping/external-group-org-role-mapping-service";
|
import { TExternalGroupOrgRoleMappingServiceFactory } from "@app/services/external-group-org-role-mapping/external-group-org-role-mapping-service";
|
||||||
@@ -83,6 +84,7 @@ import { TOrgAdminServiceFactory } from "@app/services/org-admin/org-admin-servi
|
|||||||
import { TPkiAlertServiceFactory } from "@app/services/pki-alert/pki-alert-service";
|
import { TPkiAlertServiceFactory } from "@app/services/pki-alert/pki-alert-service";
|
||||||
import { TPkiCollectionServiceFactory } from "@app/services/pki-collection/pki-collection-service";
|
import { TPkiCollectionServiceFactory } from "@app/services/pki-collection/pki-collection-service";
|
||||||
import { TPkiSubscriberServiceFactory } from "@app/services/pki-subscriber/pki-subscriber-service";
|
import { TPkiSubscriberServiceFactory } from "@app/services/pki-subscriber/pki-subscriber-service";
|
||||||
|
import { TPkiTemplatesServiceFactory } from "@app/services/pki-templates/pki-templates-service";
|
||||||
import { TProjectServiceFactory } from "@app/services/project/project-service";
|
import { TProjectServiceFactory } from "@app/services/project/project-service";
|
||||||
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
|
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
|
||||||
import { TProjectEnvServiceFactory } from "@app/services/project-env/project-env-service";
|
import { TProjectEnvServiceFactory } from "@app/services/project-env/project-env-service";
|
||||||
@@ -111,6 +113,7 @@ import { TWorkflowIntegrationServiceFactory } from "@app/services/workflow-integ
|
|||||||
declare module "@fastify/request-context" {
|
declare module "@fastify/request-context" {
|
||||||
interface RequestContextData {
|
interface RequestContextData {
|
||||||
reqId: string;
|
reqId: string;
|
||||||
|
orgId?: string;
|
||||||
identityAuthInfo?: {
|
identityAuthInfo?: {
|
||||||
identityId: string;
|
identityId: string;
|
||||||
oidc?: {
|
oidc?: {
|
||||||
@@ -270,6 +273,8 @@ declare module "fastify" {
|
|||||||
assumePrivileges: TAssumePrivilegeServiceFactory;
|
assumePrivileges: TAssumePrivilegeServiceFactory;
|
||||||
githubOrgSync: TGithubOrgSyncServiceFactory;
|
githubOrgSync: TGithubOrgSyncServiceFactory;
|
||||||
secretScanningV2: TSecretScanningV2ServiceFactory;
|
secretScanningV2: TSecretScanningV2ServiceFactory;
|
||||||
|
internalCertificateAuthority: TInternalCertificateAuthorityServiceFactory;
|
||||||
|
pkiTemplate: TPkiTemplatesServiceFactory;
|
||||||
};
|
};
|
||||||
// this is exclusive use for middlewares in which we need to inject data
|
// this is exclusive use for middlewares in which we need to inject data
|
||||||
// everywhere else access using service layer
|
// everywhere else access using service layer
|
||||||
|
|||||||
Vendored
+33
@@ -6,6 +6,9 @@ import {
|
|||||||
TAccessApprovalPoliciesApprovers,
|
TAccessApprovalPoliciesApprovers,
|
||||||
TAccessApprovalPoliciesApproversInsert,
|
TAccessApprovalPoliciesApproversInsert,
|
||||||
TAccessApprovalPoliciesApproversUpdate,
|
TAccessApprovalPoliciesApproversUpdate,
|
||||||
|
TAccessApprovalPoliciesBypassers,
|
||||||
|
TAccessApprovalPoliciesBypassersInsert,
|
||||||
|
TAccessApprovalPoliciesBypassersUpdate,
|
||||||
TAccessApprovalPoliciesInsert,
|
TAccessApprovalPoliciesInsert,
|
||||||
TAccessApprovalPoliciesUpdate,
|
TAccessApprovalPoliciesUpdate,
|
||||||
TAccessApprovalRequests,
|
TAccessApprovalRequests,
|
||||||
@@ -68,6 +71,9 @@ import {
|
|||||||
TDynamicSecrets,
|
TDynamicSecrets,
|
||||||
TDynamicSecretsInsert,
|
TDynamicSecretsInsert,
|
||||||
TDynamicSecretsUpdate,
|
TDynamicSecretsUpdate,
|
||||||
|
TExternalCertificateAuthorities,
|
||||||
|
TExternalCertificateAuthoritiesInsert,
|
||||||
|
TExternalCertificateAuthoritiesUpdate,
|
||||||
TExternalGroupOrgRoleMappings,
|
TExternalGroupOrgRoleMappings,
|
||||||
TExternalGroupOrgRoleMappingsInsert,
|
TExternalGroupOrgRoleMappingsInsert,
|
||||||
TExternalGroupOrgRoleMappingsUpdate,
|
TExternalGroupOrgRoleMappingsUpdate,
|
||||||
@@ -155,6 +161,9 @@ import {
|
|||||||
TIntegrations,
|
TIntegrations,
|
||||||
TIntegrationsInsert,
|
TIntegrationsInsert,
|
||||||
TIntegrationsUpdate,
|
TIntegrationsUpdate,
|
||||||
|
TInternalCertificateAuthorities,
|
||||||
|
TInternalCertificateAuthoritiesInsert,
|
||||||
|
TInternalCertificateAuthoritiesUpdate,
|
||||||
TInternalKms,
|
TInternalKms,
|
||||||
TInternalKmsInsert,
|
TInternalKmsInsert,
|
||||||
TInternalKmsUpdate,
|
TInternalKmsUpdate,
|
||||||
@@ -270,6 +279,9 @@ import {
|
|||||||
TSecretApprovalPoliciesApprovers,
|
TSecretApprovalPoliciesApprovers,
|
||||||
TSecretApprovalPoliciesApproversInsert,
|
TSecretApprovalPoliciesApproversInsert,
|
||||||
TSecretApprovalPoliciesApproversUpdate,
|
TSecretApprovalPoliciesApproversUpdate,
|
||||||
|
TSecretApprovalPoliciesBypassers,
|
||||||
|
TSecretApprovalPoliciesBypassersInsert,
|
||||||
|
TSecretApprovalPoliciesBypassersUpdate,
|
||||||
TSecretApprovalPoliciesInsert,
|
TSecretApprovalPoliciesInsert,
|
||||||
TSecretApprovalPoliciesUpdate,
|
TSecretApprovalPoliciesUpdate,
|
||||||
TSecretApprovalRequests,
|
TSecretApprovalRequests,
|
||||||
@@ -553,6 +565,16 @@ declare module "knex/types/tables" {
|
|||||||
TCertificateAuthorityCrlInsert,
|
TCertificateAuthorityCrlInsert,
|
||||||
TCertificateAuthorityCrlUpdate
|
TCertificateAuthorityCrlUpdate
|
||||||
>;
|
>;
|
||||||
|
[TableName.InternalCertificateAuthority]: KnexOriginal.CompositeTableType<
|
||||||
|
TInternalCertificateAuthorities,
|
||||||
|
TInternalCertificateAuthoritiesInsert,
|
||||||
|
TInternalCertificateAuthoritiesUpdate
|
||||||
|
>;
|
||||||
|
[TableName.ExternalCertificateAuthority]: KnexOriginal.CompositeTableType<
|
||||||
|
TExternalCertificateAuthorities,
|
||||||
|
TExternalCertificateAuthoritiesInsert,
|
||||||
|
TExternalCertificateAuthoritiesUpdate
|
||||||
|
>;
|
||||||
[TableName.Certificate]: KnexOriginal.CompositeTableType<TCertificates, TCertificatesInsert, TCertificatesUpdate>;
|
[TableName.Certificate]: KnexOriginal.CompositeTableType<TCertificates, TCertificatesInsert, TCertificatesUpdate>;
|
||||||
[TableName.CertificateTemplate]: KnexOriginal.CompositeTableType<
|
[TableName.CertificateTemplate]: KnexOriginal.CompositeTableType<
|
||||||
TCertificateTemplates,
|
TCertificateTemplates,
|
||||||
@@ -819,6 +841,12 @@ declare module "knex/types/tables" {
|
|||||||
TAccessApprovalPoliciesApproversUpdate
|
TAccessApprovalPoliciesApproversUpdate
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
[TableName.AccessApprovalPolicyBypasser]: KnexOriginal.CompositeTableType<
|
||||||
|
TAccessApprovalPoliciesBypassers,
|
||||||
|
TAccessApprovalPoliciesBypassersInsert,
|
||||||
|
TAccessApprovalPoliciesBypassersUpdate
|
||||||
|
>;
|
||||||
|
|
||||||
[TableName.AccessApprovalRequest]: KnexOriginal.CompositeTableType<
|
[TableName.AccessApprovalRequest]: KnexOriginal.CompositeTableType<
|
||||||
TAccessApprovalRequests,
|
TAccessApprovalRequests,
|
||||||
TAccessApprovalRequestsInsert,
|
TAccessApprovalRequestsInsert,
|
||||||
@@ -842,6 +870,11 @@ declare module "knex/types/tables" {
|
|||||||
TSecretApprovalPoliciesApproversInsert,
|
TSecretApprovalPoliciesApproversInsert,
|
||||||
TSecretApprovalPoliciesApproversUpdate
|
TSecretApprovalPoliciesApproversUpdate
|
||||||
>;
|
>;
|
||||||
|
[TableName.SecretApprovalPolicyBypasser]: KnexOriginal.CompositeTableType<
|
||||||
|
TSecretApprovalPoliciesBypassers,
|
||||||
|
TSecretApprovalPoliciesBypassersInsert,
|
||||||
|
TSecretApprovalPoliciesBypassersUpdate
|
||||||
|
>;
|
||||||
[TableName.SecretApprovalRequest]: KnexOriginal.CompositeTableType<
|
[TableName.SecretApprovalRequest]: KnexOriginal.CompositeTableType<
|
||||||
TSecretApprovalRequests,
|
TSecretApprovalRequests,
|
||||||
TSecretApprovalRequestsInsert,
|
TSecretApprovalRequestsInsert,
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasTable(TableName.Certificate)) {
|
||||||
|
const hasProjectIdColumn = await knex.schema.hasColumn(TableName.Certificate, "projectId");
|
||||||
|
if (!hasProjectIdColumn) {
|
||||||
|
await knex.schema.alterTable(TableName.Certificate, (t) => {
|
||||||
|
t.string("projectId", 36).nullable();
|
||||||
|
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.raw(`
|
||||||
|
UPDATE "${TableName.Certificate}" cert
|
||||||
|
SET "projectId" = ca."projectId"
|
||||||
|
FROM "${TableName.CertificateAuthority}" ca
|
||||||
|
WHERE cert."caId" = ca.id
|
||||||
|
`);
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.Certificate, (t) => {
|
||||||
|
t.string("projectId").notNullable().alter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.Certificate, (t) => {
|
||||||
|
t.uuid("caId").nullable().alter();
|
||||||
|
t.uuid("caCertId").nullable().alter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasTable(TableName.Certificate)) {
|
||||||
|
if (await knex.schema.hasColumn(TableName.Certificate, "projectId")) {
|
||||||
|
await knex.schema.alterTable(TableName.Certificate, (t) => {
|
||||||
|
t.dropForeign("projectId");
|
||||||
|
t.dropColumn("projectId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Altering back to notNullable for caId and caCertId will fail
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const hasEmail = await knex.schema.hasColumn(TableName.Users, "email");
|
||||||
|
const hasUsername = await knex.schema.hasColumn(TableName.Users, "username");
|
||||||
|
if (hasEmail) {
|
||||||
|
await knex(TableName.Users)
|
||||||
|
.where({ isGhost: false })
|
||||||
|
.update({
|
||||||
|
// @ts-expect-error email assume string this is expected
|
||||||
|
email: knex.raw("lower(email)")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (hasUsername) {
|
||||||
|
await knex.schema.raw(`
|
||||||
|
CREATE INDEX IF NOT EXISTS ${TableName.Users}_lower_username_idx
|
||||||
|
ON ${TableName.Users} (LOWER(username))
|
||||||
|
`);
|
||||||
|
|
||||||
|
const duplicatesSubquery = knex(TableName.Users)
|
||||||
|
.select(knex.raw("lower(username) as lowercase_username"))
|
||||||
|
.groupBy("lowercase_username")
|
||||||
|
.having(knex.raw("count(*)"), ">", 1);
|
||||||
|
|
||||||
|
// Update usernames to lowercase where they won't create duplicates
|
||||||
|
await knex(TableName.Users)
|
||||||
|
.where({ isGhost: false })
|
||||||
|
.whereRaw("username <> lower(username)") // Only update if not already lowercase
|
||||||
|
// @ts-expect-error username assume string this is expected
|
||||||
|
.whereNotIn(knex.raw("lower(username)"), duplicatesSubquery)
|
||||||
|
.update({
|
||||||
|
// @ts-expect-error username assume string this is expected
|
||||||
|
username: knex.raw("lower(username)")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const hasUsername = await knex.schema.hasColumn(TableName.Users, "username");
|
||||||
|
if (hasUsername) {
|
||||||
|
await knex.schema.raw(`
|
||||||
|
DROP INDEX IF EXISTS ${TableName.Users}_lower_username_idx
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const hasLifetimeColumn = await knex.schema.hasColumn(TableName.Organization, "maxSharedSecretLifetime");
|
||||||
|
const hasViewLimitColumn = await knex.schema.hasColumn(TableName.Organization, "maxSharedSecretViewLimit");
|
||||||
|
|
||||||
|
if (!hasLifetimeColumn || !hasViewLimitColumn) {
|
||||||
|
await knex.schema.alterTable(TableName.Organization, (t) => {
|
||||||
|
if (!hasLifetimeColumn) {
|
||||||
|
t.integer("maxSharedSecretLifetime").nullable().defaultTo(2592000); // 30 days in seconds
|
||||||
|
}
|
||||||
|
if (!hasViewLimitColumn) {
|
||||||
|
t.integer("maxSharedSecretViewLimit").nullable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const hasLifetimeColumn = await knex.schema.hasColumn(TableName.Organization, "maxSharedSecretLifetime");
|
||||||
|
const hasViewLimitColumn = await knex.schema.hasColumn(TableName.Organization, "maxSharedSecretViewLimit");
|
||||||
|
|
||||||
|
if (hasLifetimeColumn || hasViewLimitColumn) {
|
||||||
|
await knex.schema.alterTable(TableName.Organization, (t) => {
|
||||||
|
if (hasLifetimeColumn) {
|
||||||
|
t.dropColumn("maxSharedSecretLifetime");
|
||||||
|
}
|
||||||
|
if (hasViewLimitColumn) {
|
||||||
|
t.dropColumn("maxSharedSecretViewLimit");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
await knex.schema.alterTable(TableName.SecretSync, (t) => {
|
||||||
|
t.string("name", 64).notNullable().alter();
|
||||||
|
});
|
||||||
|
await knex.schema.alterTable(TableName.ProjectTemplates, (t) => {
|
||||||
|
t.string("name", 64).notNullable().alter();
|
||||||
|
});
|
||||||
|
await knex.schema.alterTable(TableName.AppConnection, (t) => {
|
||||||
|
t.string("name", 64).notNullable().alter();
|
||||||
|
});
|
||||||
|
await knex.schema.alterTable(TableName.SecretRotationV2, (t) => {
|
||||||
|
t.string("name", 64).notNullable().alter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(): Promise<void> {
|
||||||
|
// No down migration or it will error
|
||||||
|
}
|
||||||
@@ -0,0 +1,205 @@
|
|||||||
|
import slugify from "@sindresorhus/slugify";
|
||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const hasCATable = await knex.schema.hasTable(TableName.CertificateAuthority);
|
||||||
|
const hasExternalCATable = await knex.schema.hasTable(TableName.ExternalCertificateAuthority);
|
||||||
|
const hasInternalCATable = await knex.schema.hasTable(TableName.InternalCertificateAuthority);
|
||||||
|
|
||||||
|
if (hasCATable && !hasInternalCATable) {
|
||||||
|
await knex.schema.createTableLike(TableName.InternalCertificateAuthority, TableName.CertificateAuthority, (t) => {
|
||||||
|
t.uuid("caId").nullable();
|
||||||
|
});
|
||||||
|
|
||||||
|
// @ts-expect-error intentional: migration
|
||||||
|
await knex(TableName.InternalCertificateAuthority).insert(knex(TableName.CertificateAuthority).select("*"));
|
||||||
|
await knex(TableName.InternalCertificateAuthority).update("caId", knex.ref("id"));
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.InternalCertificateAuthority, (t) => {
|
||||||
|
t.dropColumn("projectId");
|
||||||
|
t.dropColumn("requireTemplateForIssuance");
|
||||||
|
t.dropColumn("createdAt");
|
||||||
|
t.dropColumn("updatedAt");
|
||||||
|
t.dropColumn("status");
|
||||||
|
t.uuid("parentCaId")
|
||||||
|
.nullable()
|
||||||
|
.references("id")
|
||||||
|
.inTable(TableName.CertificateAuthority)
|
||||||
|
.onDelete("CASCADE")
|
||||||
|
.alter();
|
||||||
|
t.uuid("activeCaCertId").nullable().references("id").inTable(TableName.CertificateAuthorityCert).alter();
|
||||||
|
t.uuid("caId").notNullable().references("id").inTable(TableName.CertificateAuthority).onDelete("CASCADE").alter();
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.CertificateAuthority, (t) => {
|
||||||
|
t.renameColumn("requireTemplateForIssuance", "enableDirectIssuance");
|
||||||
|
t.string("name").nullable();
|
||||||
|
});
|
||||||
|
|
||||||
|
// prefill name for existing internal CAs and flip enableDirectIssuance
|
||||||
|
const cas = await knex(TableName.CertificateAuthority).select("id", "friendlyName", "enableDirectIssuance");
|
||||||
|
await Promise.all(
|
||||||
|
cas.map((ca) => {
|
||||||
|
const slugifiedName = ca.friendlyName
|
||||||
|
? slugify(`${ca.friendlyName.slice(0, 16)}-${alphaNumericNanoId(8)}`)
|
||||||
|
: slugify(alphaNumericNanoId(12));
|
||||||
|
|
||||||
|
return knex(TableName.CertificateAuthority)
|
||||||
|
.where({ id: ca.id })
|
||||||
|
.update({ name: slugifiedName, enableDirectIssuance: !ca.enableDirectIssuance });
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.CertificateAuthority, (t) => {
|
||||||
|
t.dropColumn("parentCaId");
|
||||||
|
t.dropColumn("type");
|
||||||
|
t.dropColumn("friendlyName");
|
||||||
|
t.dropColumn("organization");
|
||||||
|
t.dropColumn("ou");
|
||||||
|
t.dropColumn("country");
|
||||||
|
t.dropColumn("province");
|
||||||
|
t.dropColumn("locality");
|
||||||
|
t.dropColumn("commonName");
|
||||||
|
t.dropColumn("dn");
|
||||||
|
t.dropColumn("serialNumber");
|
||||||
|
t.dropColumn("maxPathLength");
|
||||||
|
t.dropColumn("keyAlgorithm");
|
||||||
|
t.dropColumn("notBefore");
|
||||||
|
t.dropColumn("notAfter");
|
||||||
|
t.dropColumn("activeCaCertId");
|
||||||
|
t.boolean("enableDirectIssuance").notNullable().defaultTo(true).alter();
|
||||||
|
t.string("name").notNullable().alter();
|
||||||
|
t.unique(["name", "projectId"]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasExternalCATable) {
|
||||||
|
await knex.schema.createTable(TableName.ExternalCertificateAuthority, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
t.string("type").notNullable();
|
||||||
|
t.uuid("appConnectionId").nullable();
|
||||||
|
t.foreign("appConnectionId").references("id").inTable(TableName.AppConnection);
|
||||||
|
t.uuid("dnsAppConnectionId").nullable();
|
||||||
|
t.foreign("dnsAppConnectionId").references("id").inTable(TableName.AppConnection);
|
||||||
|
t.uuid("caId").notNullable().references("id").inTable(TableName.CertificateAuthority).onDelete("CASCADE");
|
||||||
|
t.binary("credentials");
|
||||||
|
t.json("configuration");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await knex.schema.hasTable(TableName.PkiSubscriber)) {
|
||||||
|
await knex.schema.alterTable(TableName.PkiSubscriber, (t) => {
|
||||||
|
t.string("ttl").nullable().alter();
|
||||||
|
|
||||||
|
t.boolean("enableAutoRenewal").notNullable().defaultTo(false);
|
||||||
|
t.integer("autoRenewalPeriodInDays");
|
||||||
|
t.datetime("lastAutoRenewAt");
|
||||||
|
|
||||||
|
t.string("lastOperationStatus");
|
||||||
|
t.text("lastOperationMessage");
|
||||||
|
t.dateTime("lastOperationAt");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const hasCATable = await knex.schema.hasTable(TableName.CertificateAuthority);
|
||||||
|
const hasExternalCATable = await knex.schema.hasTable(TableName.ExternalCertificateAuthority);
|
||||||
|
const hasInternalCATable = await knex.schema.hasTable(TableName.InternalCertificateAuthority);
|
||||||
|
|
||||||
|
if (hasCATable && hasInternalCATable) {
|
||||||
|
// First add all columns as nullable
|
||||||
|
await knex.schema.alterTable(TableName.CertificateAuthority, (t) => {
|
||||||
|
t.uuid("parentCaId").nullable().references("id").inTable(TableName.CertificateAuthority).onDelete("CASCADE");
|
||||||
|
t.string("type").nullable();
|
||||||
|
t.string("friendlyName").nullable();
|
||||||
|
t.string("organization").nullable();
|
||||||
|
t.string("ou").nullable();
|
||||||
|
t.string("country").nullable();
|
||||||
|
t.string("province").nullable();
|
||||||
|
t.string("locality").nullable();
|
||||||
|
t.string("commonName").nullable();
|
||||||
|
t.string("dn").nullable();
|
||||||
|
t.string("serialNumber").nullable().unique();
|
||||||
|
t.integer("maxPathLength").nullable();
|
||||||
|
t.string("keyAlgorithm").nullable();
|
||||||
|
t.timestamp("notBefore").nullable();
|
||||||
|
t.timestamp("notAfter").nullable();
|
||||||
|
t.uuid("activeCaCertId").nullable().references("id").inTable(TableName.CertificateAuthorityCert);
|
||||||
|
t.renameColumn("enableDirectIssuance", "requireTemplateForIssuance");
|
||||||
|
t.dropColumn("name");
|
||||||
|
});
|
||||||
|
|
||||||
|
// flip requireTemplateForIssuance for existing internal CAs
|
||||||
|
const cas = await knex(TableName.CertificateAuthority).select("id", "requireTemplateForIssuance");
|
||||||
|
await Promise.all(
|
||||||
|
cas.map((ca) => {
|
||||||
|
return (
|
||||||
|
knex(TableName.CertificateAuthority)
|
||||||
|
.where({ id: ca.id })
|
||||||
|
// @ts-expect-error intentional: migration
|
||||||
|
.update({ requireTemplateForIssuance: !ca.requireTemplateForIssuance })
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await knex.raw(`
|
||||||
|
UPDATE ${TableName.CertificateAuthority} ca
|
||||||
|
SET
|
||||||
|
type = ica.type,
|
||||||
|
"friendlyName" = ica."friendlyName",
|
||||||
|
organization = ica.organization,
|
||||||
|
ou = ica.ou,
|
||||||
|
country = ica.country,
|
||||||
|
province = ica.province,
|
||||||
|
locality = ica.locality,
|
||||||
|
"commonName" = ica."commonName",
|
||||||
|
dn = ica.dn,
|
||||||
|
"parentCaId" = ica."parentCaId",
|
||||||
|
"serialNumber" = ica."serialNumber",
|
||||||
|
"maxPathLength" = ica."maxPathLength",
|
||||||
|
"keyAlgorithm" = ica."keyAlgorithm",
|
||||||
|
"notBefore" = ica."notBefore",
|
||||||
|
"notAfter" = ica."notAfter",
|
||||||
|
"activeCaCertId" = ica."activeCaCertId"
|
||||||
|
FROM ${TableName.InternalCertificateAuthority} ica
|
||||||
|
WHERE ca.id = ica."caId"
|
||||||
|
`);
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.CertificateAuthority, (t) => {
|
||||||
|
t.string("type").notNullable().alter();
|
||||||
|
t.string("friendlyName").notNullable().alter();
|
||||||
|
t.string("organization").notNullable().alter();
|
||||||
|
t.string("ou").notNullable().alter();
|
||||||
|
t.string("country").notNullable().alter();
|
||||||
|
t.string("province").notNullable().alter();
|
||||||
|
t.string("locality").notNullable().alter();
|
||||||
|
t.string("commonName").notNullable().alter();
|
||||||
|
t.string("dn").notNullable().alter();
|
||||||
|
t.string("keyAlgorithm").notNullable().alter();
|
||||||
|
t.boolean("requireTemplateForIssuance").notNullable().defaultTo(false).alter();
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.dropTable(TableName.InternalCertificateAuthority);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasExternalCATable) {
|
||||||
|
await knex.schema.dropTable(TableName.ExternalCertificateAuthority);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await knex.schema.hasTable(TableName.PkiSubscriber)) {
|
||||||
|
await knex.schema.alterTable(TableName.PkiSubscriber, (t) => {
|
||||||
|
t.dropColumn("enableAutoRenewal");
|
||||||
|
t.dropColumn("autoRenewalPeriodInDays");
|
||||||
|
t.dropColumn("lastAutoRenewAt");
|
||||||
|
|
||||||
|
t.dropColumn("lastOperationStatus");
|
||||||
|
t.dropColumn("lastOperationMessage");
|
||||||
|
t.dropColumn("lastOperationAt");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasTable(TableName.AccessApprovalPolicyBypasser))) {
|
||||||
|
await knex.schema.createTable(TableName.AccessApprovalPolicyBypasser, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
|
||||||
|
t.uuid("bypasserGroupId").nullable();
|
||||||
|
t.foreign("bypasserGroupId").references("id").inTable(TableName.Groups).onDelete("CASCADE");
|
||||||
|
|
||||||
|
t.uuid("bypasserUserId").nullable();
|
||||||
|
t.foreign("bypasserUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
|
||||||
|
|
||||||
|
t.uuid("policyId").notNullable();
|
||||||
|
t.foreign("policyId").references("id").inTable(TableName.AccessApprovalPolicy).onDelete("CASCADE");
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
});
|
||||||
|
await createOnUpdateTrigger(knex, TableName.AccessApprovalPolicyBypasser);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await knex.schema.hasTable(TableName.SecretApprovalPolicyBypasser))) {
|
||||||
|
await knex.schema.createTable(TableName.SecretApprovalPolicyBypasser, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
|
||||||
|
t.uuid("bypasserGroupId").nullable();
|
||||||
|
t.foreign("bypasserGroupId").references("id").inTable(TableName.Groups).onDelete("CASCADE");
|
||||||
|
|
||||||
|
t.uuid("bypasserUserId").nullable();
|
||||||
|
t.foreign("bypasserUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
|
||||||
|
|
||||||
|
t.uuid("policyId").notNullable();
|
||||||
|
t.foreign("policyId").references("id").inTable(TableName.SecretApprovalPolicy).onDelete("CASCADE");
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
});
|
||||||
|
await createOnUpdateTrigger(knex, TableName.SecretApprovalPolicyBypasser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
await knex.schema.dropTableIfExists(TableName.SecretApprovalPolicyBypasser);
|
||||||
|
await knex.schema.dropTableIfExists(TableName.AccessApprovalPolicyBypasser);
|
||||||
|
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.SecretApprovalPolicyBypasser);
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.AccessApprovalPolicyBypasser);
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const hasColumn = await knex.schema.hasColumn(TableName.DynamicSecret, "usernameTemplate");
|
||||||
|
if (!hasColumn) {
|
||||||
|
await knex.schema.alterTable(TableName.DynamicSecret, (t) => {
|
||||||
|
t.string("usernameTemplate").nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const hasColumn = await knex.schema.hasColumn(TableName.DynamicSecret, "usernameTemplate");
|
||||||
|
if (hasColumn) {
|
||||||
|
await knex.schema.alterTable(TableName.DynamicSecret, (t) => {
|
||||||
|
t.dropColumn("usernameTemplate");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.IdentityAccessToken, "accessTokenPeriod"))) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityAccessToken, (t) => {
|
||||||
|
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.IdentityUniversalAuth, "accessTokenPeriod"))) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityUniversalAuth, (t) => {
|
||||||
|
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.IdentityAwsAuth, "accessTokenPeriod"))) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityAwsAuth, (t) => {
|
||||||
|
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.IdentityOidcAuth, "accessTokenPeriod"))) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityOidcAuth, (t) => {
|
||||||
|
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.IdentityAzureAuth, "accessTokenPeriod"))) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityAzureAuth, (t) => {
|
||||||
|
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.IdentityGcpAuth, "accessTokenPeriod"))) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityGcpAuth, (t) => {
|
||||||
|
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.IdentityJwtAuth, "accessTokenPeriod"))) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityJwtAuth, (t) => {
|
||||||
|
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.IdentityKubernetesAuth, "accessTokenPeriod"))) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityKubernetesAuth, (t) => {
|
||||||
|
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.IdentityLdapAuth, "accessTokenPeriod"))) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityLdapAuth, (t) => {
|
||||||
|
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.IdentityOciAuth, "accessTokenPeriod"))) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityOciAuth, (t) => {
|
||||||
|
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.IdentityTokenAuth, "accessTokenPeriod"))) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityTokenAuth, (t) => {
|
||||||
|
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasColumn(TableName.IdentityAccessToken, "accessTokenPeriod")) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityAccessToken, (t) => {
|
||||||
|
t.dropColumn("accessTokenPeriod");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await knex.schema.hasColumn(TableName.IdentityUniversalAuth, "accessTokenPeriod")) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityUniversalAuth, (t) => {
|
||||||
|
t.dropColumn("accessTokenPeriod");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await knex.schema.hasColumn(TableName.IdentityAwsAuth, "accessTokenPeriod")) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityAwsAuth, (t) => {
|
||||||
|
t.dropColumn("accessTokenPeriod");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await knex.schema.hasColumn(TableName.IdentityOidcAuth, "accessTokenPeriod")) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityOidcAuth, (t) => {
|
||||||
|
t.dropColumn("accessTokenPeriod");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await knex.schema.hasColumn(TableName.IdentityAzureAuth, "accessTokenPeriod")) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityAzureAuth, (t) => {
|
||||||
|
t.dropColumn("accessTokenPeriod");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await knex.schema.hasColumn(TableName.IdentityGcpAuth, "accessTokenPeriod")) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityGcpAuth, (t) => {
|
||||||
|
t.dropColumn("accessTokenPeriod");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await knex.schema.hasColumn(TableName.IdentityJwtAuth, "accessTokenPeriod")) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityJwtAuth, (t) => {
|
||||||
|
t.dropColumn("accessTokenPeriod");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await knex.schema.hasColumn(TableName.IdentityKubernetesAuth, "accessTokenPeriod")) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityKubernetesAuth, (t) => {
|
||||||
|
t.dropColumn("accessTokenPeriod");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await knex.schema.hasColumn(TableName.IdentityLdapAuth, "accessTokenPeriod")) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityLdapAuth, (t) => {
|
||||||
|
t.dropColumn("accessTokenPeriod");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await knex.schema.hasColumn(TableName.IdentityOciAuth, "accessTokenPeriod")) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityOciAuth, (t) => {
|
||||||
|
t.dropColumn("accessTokenPeriod");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await knex.schema.hasColumn(TableName.IdentityTokenAuth, "accessTokenPeriod")) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityTokenAuth, (t) => {
|
||||||
|
t.dropColumn("accessTokenPeriod");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import slugify from "@sindresorhus/slugify";
|
||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const hasNameCol = await knex.schema.hasColumn(TableName.CertificateTemplate, "name");
|
||||||
|
if (hasNameCol) {
|
||||||
|
const templates = await knex(TableName.CertificateTemplate).select("id", "name");
|
||||||
|
await Promise.all(
|
||||||
|
templates.map((el) => {
|
||||||
|
const slugifiedName = el.name
|
||||||
|
? slugify(`${el.name.slice(0, 16)}-${alphaNumericNanoId(8)}`)
|
||||||
|
: slugify(alphaNumericNanoId(12));
|
||||||
|
|
||||||
|
return knex(TableName.CertificateTemplate).where({ id: el.id }).update({ name: slugifiedName });
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(): Promise<void> {}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasTable(TableName.SecretSharing)) {
|
||||||
|
const hasEncryptedSalt = await knex.schema.hasColumn(TableName.SecretSharing, "encryptedSalt");
|
||||||
|
|
||||||
|
if (hasEncryptedSalt) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
|
||||||
|
t.dropColumn("encryptedSalt");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasTable(TableName.SecretSharing)) {
|
||||||
|
const hasEncryptedSalt = await knex.schema.hasColumn(TableName.SecretSharing, "encryptedSalt");
|
||||||
|
|
||||||
|
if (!hasEncryptedSalt) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
|
||||||
|
t.binary("encryptedSalt").nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { ApprovalStatus } from "@app/ee/services/secret-approval-request/secret-approval-request-types";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const hasPrivilegeDeletedAtColumn = await knex.schema.hasColumn(
|
||||||
|
TableName.AccessApprovalRequest,
|
||||||
|
"privilegeDeletedAt"
|
||||||
|
);
|
||||||
|
const hasStatusColumn = await knex.schema.hasColumn(TableName.AccessApprovalRequest, "status");
|
||||||
|
|
||||||
|
if (!hasPrivilegeDeletedAtColumn) {
|
||||||
|
await knex.schema.alterTable(TableName.AccessApprovalRequest, (t) => {
|
||||||
|
t.timestamp("privilegeDeletedAt").nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasStatusColumn) {
|
||||||
|
await knex.schema.alterTable(TableName.AccessApprovalRequest, (t) => {
|
||||||
|
t.string("status").defaultTo(ApprovalStatus.PENDING).notNullable();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update existing rows based on business logic
|
||||||
|
// If privilegeId is not null, set status to "approved"
|
||||||
|
await knex(TableName.AccessApprovalRequest).whereNotNull("privilegeId").update({ status: ApprovalStatus.APPROVED });
|
||||||
|
|
||||||
|
// If privilegeId is null and there's a rejected reviewer, set to "rejected"
|
||||||
|
const rejectedRequestIds = await knex(TableName.AccessApprovalRequestReviewer)
|
||||||
|
.select("requestId")
|
||||||
|
.where("status", "rejected")
|
||||||
|
.distinct()
|
||||||
|
.pluck("requestId");
|
||||||
|
|
||||||
|
if (rejectedRequestIds.length > 0) {
|
||||||
|
await knex(TableName.AccessApprovalRequest)
|
||||||
|
.whereNull("privilegeId")
|
||||||
|
.whereIn("id", rejectedRequestIds)
|
||||||
|
.update({ status: ApprovalStatus.REJECTED });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const hasPrivilegeDeletedAtColumn = await knex.schema.hasColumn(
|
||||||
|
TableName.AccessApprovalRequest,
|
||||||
|
"privilegeDeletedAt"
|
||||||
|
);
|
||||||
|
const hasStatusColumn = await knex.schema.hasColumn(TableName.AccessApprovalRequest, "status");
|
||||||
|
|
||||||
|
if (hasPrivilegeDeletedAtColumn) {
|
||||||
|
await knex.schema.alterTable(TableName.AccessApprovalRequest, (t) => {
|
||||||
|
t.dropColumn("privilegeDeletedAt");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasStatusColumn) {
|
||||||
|
await knex.schema.alterTable(TableName.AccessApprovalRequest, (t) => {
|
||||||
|
t.dropColumn("status");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
// Code generated by automation script, DO NOT EDIT.
|
||||||
|
// Automated by pulling database and generating zod schema
|
||||||
|
// To update. Just run npm run generate:schema
|
||||||
|
// Written by akhilmhdh.
|
||||||
|
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { TImmutableDBKeys } from "./models";
|
||||||
|
|
||||||
|
export const AccessApprovalPoliciesBypassersSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
bypasserGroupId: z.string().uuid().nullable().optional(),
|
||||||
|
bypasserUserId: z.string().uuid().nullable().optional(),
|
||||||
|
policyId: z.string().uuid(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TAccessApprovalPoliciesBypassers = z.infer<typeof AccessApprovalPoliciesBypassersSchema>;
|
||||||
|
export type TAccessApprovalPoliciesBypassersInsert = Omit<
|
||||||
|
z.input<typeof AccessApprovalPoliciesBypassersSchema>,
|
||||||
|
TImmutableDBKeys
|
||||||
|
>;
|
||||||
|
export type TAccessApprovalPoliciesBypassersUpdate = Partial<
|
||||||
|
Omit<z.input<typeof AccessApprovalPoliciesBypassersSchema>, TImmutableDBKeys>
|
||||||
|
>;
|
||||||
@@ -18,7 +18,9 @@ export const AccessApprovalRequestsSchema = z.object({
|
|||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
requestedByUserId: z.string().uuid(),
|
requestedByUserId: z.string().uuid(),
|
||||||
note: z.string().nullable().optional()
|
note: z.string().nullable().optional(),
|
||||||
|
privilegeDeletedAt: z.date().nullable().optional(),
|
||||||
|
status: z.string().default("pending")
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TAccessApprovalRequests = z.infer<typeof AccessApprovalRequestsSchema>;
|
export type TAccessApprovalRequests = z.infer<typeof AccessApprovalRequestsSchema>;
|
||||||
|
|||||||
@@ -11,25 +11,10 @@ export const CertificateAuthoritiesSchema = z.object({
|
|||||||
id: z.string().uuid(),
|
id: z.string().uuid(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
parentCaId: z.string().uuid().nullable().optional(),
|
|
||||||
projectId: z.string(),
|
projectId: z.string(),
|
||||||
type: z.string(),
|
enableDirectIssuance: z.boolean().default(true),
|
||||||
status: z.string(),
|
status: z.string(),
|
||||||
friendlyName: z.string(),
|
name: z.string()
|
||||||
organization: z.string(),
|
|
||||||
ou: z.string(),
|
|
||||||
country: z.string(),
|
|
||||||
province: z.string(),
|
|
||||||
locality: z.string(),
|
|
||||||
commonName: z.string(),
|
|
||||||
dn: z.string(),
|
|
||||||
serialNumber: z.string().nullable().optional(),
|
|
||||||
maxPathLength: z.number().nullable().optional(),
|
|
||||||
keyAlgorithm: z.string(),
|
|
||||||
notBefore: z.date().nullable().optional(),
|
|
||||||
notAfter: z.date().nullable().optional(),
|
|
||||||
activeCaCertId: z.string().uuid().nullable().optional(),
|
|
||||||
requireTemplateForIssuance: z.boolean().default(false)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TCertificateAuthorities = z.infer<typeof CertificateAuthoritiesSchema>;
|
export type TCertificateAuthorities = z.infer<typeof CertificateAuthoritiesSchema>;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export const CertificatesSchema = z.object({
|
|||||||
id: z.string().uuid(),
|
id: z.string().uuid(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
caId: z.string().uuid(),
|
caId: z.string().uuid().nullable().optional(),
|
||||||
status: z.string(),
|
status: z.string(),
|
||||||
serialNumber: z.string(),
|
serialNumber: z.string(),
|
||||||
friendlyName: z.string(),
|
friendlyName: z.string(),
|
||||||
@@ -21,11 +21,12 @@ export const CertificatesSchema = z.object({
|
|||||||
revokedAt: z.date().nullable().optional(),
|
revokedAt: z.date().nullable().optional(),
|
||||||
revocationReason: z.number().nullable().optional(),
|
revocationReason: z.number().nullable().optional(),
|
||||||
altNames: z.string().nullable().optional(),
|
altNames: z.string().nullable().optional(),
|
||||||
caCertId: z.string().uuid(),
|
caCertId: z.string().uuid().nullable().optional(),
|
||||||
certificateTemplateId: z.string().uuid().nullable().optional(),
|
certificateTemplateId: z.string().uuid().nullable().optional(),
|
||||||
keyUsages: z.string().array().nullable().optional(),
|
keyUsages: z.string().array().nullable().optional(),
|
||||||
extendedKeyUsages: z.string().array().nullable().optional(),
|
extendedKeyUsages: z.string().array().nullable().optional(),
|
||||||
pkiSubscriberId: z.string().uuid().nullable().optional()
|
pkiSubscriberId: z.string().uuid().nullable().optional(),
|
||||||
|
projectId: z.string()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TCertificates = z.infer<typeof CertificatesSchema>;
|
export type TCertificates = z.infer<typeof CertificatesSchema>;
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ export const DynamicSecretsSchema = z.object({
|
|||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
encryptedInput: zodBuffer,
|
encryptedInput: zodBuffer,
|
||||||
projectGatewayId: z.string().uuid().nullable().optional(),
|
projectGatewayId: z.string().uuid().nullable().optional(),
|
||||||
gatewayId: z.string().uuid().nullable().optional()
|
gatewayId: z.string().uuid().nullable().optional(),
|
||||||
|
usernameTemplate: z.string().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TDynamicSecrets = z.infer<typeof DynamicSecretsSchema>;
|
export type TDynamicSecrets = z.infer<typeof DynamicSecretsSchema>;
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// Code generated by automation script, DO NOT EDIT.
|
||||||
|
// Automated by pulling database and generating zod schema
|
||||||
|
// To update. Just run npm run generate:schema
|
||||||
|
// Written by akhilmhdh.
|
||||||
|
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { zodBuffer } from "@app/lib/zod";
|
||||||
|
|
||||||
|
import { TImmutableDBKeys } from "./models";
|
||||||
|
|
||||||
|
export const ExternalCertificateAuthoritiesSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
type: z.string(),
|
||||||
|
appConnectionId: z.string().uuid().nullable().optional(),
|
||||||
|
dnsAppConnectionId: z.string().uuid().nullable().optional(),
|
||||||
|
caId: z.string().uuid(),
|
||||||
|
credentials: zodBuffer.nullable().optional(),
|
||||||
|
configuration: z.unknown().nullable().optional()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TExternalCertificateAuthorities = z.infer<typeof ExternalCertificateAuthoritiesSchema>;
|
||||||
|
export type TExternalCertificateAuthoritiesInsert = Omit<
|
||||||
|
z.input<typeof ExternalCertificateAuthoritiesSchema>,
|
||||||
|
TImmutableDBKeys
|
||||||
|
>;
|
||||||
|
export type TExternalCertificateAuthoritiesUpdate = Partial<
|
||||||
|
Omit<z.input<typeof ExternalCertificateAuthoritiesSchema>, TImmutableDBKeys>
|
||||||
|
>;
|
||||||
@@ -21,7 +21,8 @@ export const IdentityAccessTokensSchema = z.object({
|
|||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
name: z.string().nullable().optional(),
|
name: z.string().nullable().optional(),
|
||||||
authMethod: z.string()
|
authMethod: z.string(),
|
||||||
|
accessTokenPeriod: z.coerce.number().default(0)
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TIdentityAccessTokens = z.infer<typeof IdentityAccessTokensSchema>;
|
export type TIdentityAccessTokens = z.infer<typeof IdentityAccessTokensSchema>;
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ export const IdentityAwsAuthsSchema = z.object({
|
|||||||
type: z.string(),
|
type: z.string(),
|
||||||
stsEndpoint: z.string(),
|
stsEndpoint: z.string(),
|
||||||
allowedPrincipalArns: z.string(),
|
allowedPrincipalArns: z.string(),
|
||||||
allowedAccountIds: z.string()
|
allowedAccountIds: z.string(),
|
||||||
|
accessTokenPeriod: z.coerce.number().default(0)
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TIdentityAwsAuths = z.infer<typeof IdentityAwsAuthsSchema>;
|
export type TIdentityAwsAuths = z.infer<typeof IdentityAwsAuthsSchema>;
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ export const IdentityAzureAuthsSchema = z.object({
|
|||||||
identityId: z.string().uuid(),
|
identityId: z.string().uuid(),
|
||||||
tenantId: z.string(),
|
tenantId: z.string(),
|
||||||
resource: z.string(),
|
resource: z.string(),
|
||||||
allowedServicePrincipalIds: z.string()
|
allowedServicePrincipalIds: z.string(),
|
||||||
|
accessTokenPeriod: z.coerce.number().default(0)
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TIdentityAzureAuths = z.infer<typeof IdentityAzureAuthsSchema>;
|
export type TIdentityAzureAuths = z.infer<typeof IdentityAzureAuthsSchema>;
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ export const IdentityGcpAuthsSchema = z.object({
|
|||||||
type: z.string(),
|
type: z.string(),
|
||||||
allowedServiceAccounts: z.string().nullable().optional(),
|
allowedServiceAccounts: z.string().nullable().optional(),
|
||||||
allowedProjects: z.string().nullable().optional(),
|
allowedProjects: z.string().nullable().optional(),
|
||||||
allowedZones: z.string().nullable().optional()
|
allowedZones: z.string().nullable().optional(),
|
||||||
|
accessTokenPeriod: z.coerce.number().default(0)
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TIdentityGcpAuths = z.infer<typeof IdentityGcpAuthsSchema>;
|
export type TIdentityGcpAuths = z.infer<typeof IdentityGcpAuthsSchema>;
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ export const IdentityJwtAuthsSchema = z.object({
|
|||||||
boundClaims: z.unknown(),
|
boundClaims: z.unknown(),
|
||||||
boundSubject: z.string(),
|
boundSubject: z.string(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date()
|
updatedAt: z.date(),
|
||||||
|
accessTokenPeriod: z.coerce.number().default(0)
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TIdentityJwtAuths = z.infer<typeof IdentityJwtAuthsSchema>;
|
export type TIdentityJwtAuths = z.infer<typeof IdentityJwtAuthsSchema>;
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ export const IdentityKubernetesAuthsSchema = z.object({
|
|||||||
allowedAudience: z.string(),
|
allowedAudience: z.string(),
|
||||||
encryptedKubernetesTokenReviewerJwt: zodBuffer.nullable().optional(),
|
encryptedKubernetesTokenReviewerJwt: zodBuffer.nullable().optional(),
|
||||||
encryptedKubernetesCaCertificate: zodBuffer.nullable().optional(),
|
encryptedKubernetesCaCertificate: zodBuffer.nullable().optional(),
|
||||||
gatewayId: z.string().uuid().nullable().optional()
|
gatewayId: z.string().uuid().nullable().optional(),
|
||||||
|
accessTokenPeriod: z.coerce.number().default(0)
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TIdentityKubernetesAuths = z.infer<typeof IdentityKubernetesAuthsSchema>;
|
export type TIdentityKubernetesAuths = z.infer<typeof IdentityKubernetesAuthsSchema>;
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ export const IdentityLdapAuthsSchema = z.object({
|
|||||||
searchFilter: z.string(),
|
searchFilter: z.string(),
|
||||||
allowedFields: z.unknown().nullable().optional(),
|
allowedFields: z.unknown().nullable().optional(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date()
|
updatedAt: z.date(),
|
||||||
|
accessTokenPeriod: z.coerce.number().default(0)
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TIdentityLdapAuths = z.infer<typeof IdentityLdapAuthsSchema>;
|
export type TIdentityLdapAuths = z.infer<typeof IdentityLdapAuthsSchema>;
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ export const IdentityOciAuthsSchema = z.object({
|
|||||||
identityId: z.string().uuid(),
|
identityId: z.string().uuid(),
|
||||||
type: z.string(),
|
type: z.string(),
|
||||||
tenancyOcid: z.string(),
|
tenancyOcid: z.string(),
|
||||||
allowedUsernames: z.string().nullable().optional()
|
allowedUsernames: z.string().nullable().optional(),
|
||||||
|
accessTokenPeriod: z.coerce.number().default(0)
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TIdentityOciAuths = z.infer<typeof IdentityOciAuthsSchema>;
|
export type TIdentityOciAuths = z.infer<typeof IdentityOciAuthsSchema>;
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ export const IdentityOidcAuthsSchema = z.object({
|
|||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
encryptedCaCertificate: zodBuffer.nullable().optional(),
|
encryptedCaCertificate: zodBuffer.nullable().optional(),
|
||||||
claimMetadataMapping: z.unknown().nullable().optional()
|
claimMetadataMapping: z.unknown().nullable().optional(),
|
||||||
|
accessTokenPeriod: z.coerce.number().default(0)
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TIdentityOidcAuths = z.infer<typeof IdentityOidcAuthsSchema>;
|
export type TIdentityOidcAuths = z.infer<typeof IdentityOidcAuthsSchema>;
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ export const IdentityTokenAuthsSchema = z.object({
|
|||||||
accessTokenTrustedIps: z.unknown(),
|
accessTokenTrustedIps: z.unknown(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
identityId: z.string().uuid()
|
identityId: z.string().uuid(),
|
||||||
|
accessTokenPeriod: z.coerce.number().default(0)
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TIdentityTokenAuths = z.infer<typeof IdentityTokenAuthsSchema>;
|
export type TIdentityTokenAuths = z.infer<typeof IdentityTokenAuthsSchema>;
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ export const IdentityUniversalAuthsSchema = z.object({
|
|||||||
accessTokenTrustedIps: z.unknown(),
|
accessTokenTrustedIps: z.unknown(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
identityId: z.string().uuid()
|
identityId: z.string().uuid(),
|
||||||
|
accessTokenPeriod: z.coerce.number().default(0)
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TIdentityUniversalAuths = z.infer<typeof IdentityUniversalAuthsSchema>;
|
export type TIdentityUniversalAuths = z.infer<typeof IdentityUniversalAuthsSchema>;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
export * from "./access-approval-policies";
|
export * from "./access-approval-policies";
|
||||||
export * from "./access-approval-policies-approvers";
|
export * from "./access-approval-policies-approvers";
|
||||||
|
export * from "./access-approval-policies-bypassers";
|
||||||
export * from "./access-approval-requests";
|
export * from "./access-approval-requests";
|
||||||
export * from "./access-approval-requests-reviewers";
|
export * from "./access-approval-requests-reviewers";
|
||||||
export * from "./api-keys";
|
export * from "./api-keys";
|
||||||
@@ -20,6 +21,7 @@ export * from "./certificate-templates";
|
|||||||
export * from "./certificates";
|
export * from "./certificates";
|
||||||
export * from "./dynamic-secret-leases";
|
export * from "./dynamic-secret-leases";
|
||||||
export * from "./dynamic-secrets";
|
export * from "./dynamic-secrets";
|
||||||
|
export * from "./external-certificate-authorities";
|
||||||
export * from "./external-group-org-role-mappings";
|
export * from "./external-group-org-role-mappings";
|
||||||
export * from "./external-kms";
|
export * from "./external-kms";
|
||||||
export * from "./gateways";
|
export * from "./gateways";
|
||||||
@@ -49,6 +51,7 @@ export * from "./identity-universal-auths";
|
|||||||
export * from "./incident-contacts";
|
export * from "./incident-contacts";
|
||||||
export * from "./integration-auths";
|
export * from "./integration-auths";
|
||||||
export * from "./integrations";
|
export * from "./integrations";
|
||||||
|
export * from "./internal-certificate-authorities";
|
||||||
export * from "./internal-kms";
|
export * from "./internal-kms";
|
||||||
export * from "./kmip-client-certificates";
|
export * from "./kmip-client-certificates";
|
||||||
export * from "./kmip-clients";
|
export * from "./kmip-clients";
|
||||||
@@ -90,6 +93,7 @@ export * from "./saml-configs";
|
|||||||
export * from "./scim-tokens";
|
export * from "./scim-tokens";
|
||||||
export * from "./secret-approval-policies";
|
export * from "./secret-approval-policies";
|
||||||
export * from "./secret-approval-policies-approvers";
|
export * from "./secret-approval-policies-approvers";
|
||||||
|
export * from "./secret-approval-policies-bypassers";
|
||||||
export * from "./secret-approval-request-secret-tags";
|
export * from "./secret-approval-request-secret-tags";
|
||||||
export * from "./secret-approval-request-secret-tags-v2";
|
export * from "./secret-approval-request-secret-tags-v2";
|
||||||
export * from "./secret-approval-requests";
|
export * from "./secret-approval-requests";
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// Code generated by automation script, DO NOT EDIT.
|
||||||
|
// Automated by pulling database and generating zod schema
|
||||||
|
// To update. Just run npm run generate:schema
|
||||||
|
// Written by akhilmhdh.
|
||||||
|
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { TImmutableDBKeys } from "./models";
|
||||||
|
|
||||||
|
export const InternalCertificateAuthoritiesSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
parentCaId: z.string().uuid().nullable().optional(),
|
||||||
|
type: z.string(),
|
||||||
|
friendlyName: z.string(),
|
||||||
|
organization: z.string(),
|
||||||
|
ou: z.string(),
|
||||||
|
country: z.string(),
|
||||||
|
province: z.string(),
|
||||||
|
locality: z.string(),
|
||||||
|
commonName: z.string(),
|
||||||
|
dn: z.string(),
|
||||||
|
serialNumber: z.string().nullable().optional(),
|
||||||
|
maxPathLength: z.number().nullable().optional(),
|
||||||
|
keyAlgorithm: z.string(),
|
||||||
|
notBefore: z.date().nullable().optional(),
|
||||||
|
notAfter: z.date().nullable().optional(),
|
||||||
|
activeCaCertId: z.string().uuid().nullable().optional(),
|
||||||
|
caId: z.string().uuid()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TInternalCertificateAuthorities = z.infer<typeof InternalCertificateAuthoritiesSchema>;
|
||||||
|
export type TInternalCertificateAuthoritiesInsert = Omit<
|
||||||
|
z.input<typeof InternalCertificateAuthoritiesSchema>,
|
||||||
|
TImmutableDBKeys
|
||||||
|
>;
|
||||||
|
export type TInternalCertificateAuthoritiesUpdate = Partial<
|
||||||
|
Omit<z.input<typeof InternalCertificateAuthoritiesSchema>, TImmutableDBKeys>
|
||||||
|
>;
|
||||||
@@ -13,6 +13,8 @@ export enum TableName {
|
|||||||
SshCertificate = "ssh_certificates",
|
SshCertificate = "ssh_certificates",
|
||||||
SshCertificateBody = "ssh_certificate_bodies",
|
SshCertificateBody = "ssh_certificate_bodies",
|
||||||
CertificateAuthority = "certificate_authorities",
|
CertificateAuthority = "certificate_authorities",
|
||||||
|
ExternalCertificateAuthority = "external_certificate_authorities",
|
||||||
|
InternalCertificateAuthority = "internal_certificate_authorities",
|
||||||
CertificateTemplateEstConfig = "certificate_template_est_configs",
|
CertificateTemplateEstConfig = "certificate_template_est_configs",
|
||||||
CertificateAuthorityCert = "certificate_authority_certs",
|
CertificateAuthorityCert = "certificate_authority_certs",
|
||||||
CertificateAuthoritySecret = "certificate_authority_secret",
|
CertificateAuthoritySecret = "certificate_authority_secret",
|
||||||
@@ -93,10 +95,12 @@ export enum TableName {
|
|||||||
ScimToken = "scim_tokens",
|
ScimToken = "scim_tokens",
|
||||||
AccessApprovalPolicy = "access_approval_policies",
|
AccessApprovalPolicy = "access_approval_policies",
|
||||||
AccessApprovalPolicyApprover = "access_approval_policies_approvers",
|
AccessApprovalPolicyApprover = "access_approval_policies_approvers",
|
||||||
|
AccessApprovalPolicyBypasser = "access_approval_policies_bypassers",
|
||||||
AccessApprovalRequest = "access_approval_requests",
|
AccessApprovalRequest = "access_approval_requests",
|
||||||
AccessApprovalRequestReviewer = "access_approval_requests_reviewers",
|
AccessApprovalRequestReviewer = "access_approval_requests_reviewers",
|
||||||
SecretApprovalPolicy = "secret_approval_policies",
|
SecretApprovalPolicy = "secret_approval_policies",
|
||||||
SecretApprovalPolicyApprover = "secret_approval_policies_approvers",
|
SecretApprovalPolicyApprover = "secret_approval_policies_approvers",
|
||||||
|
SecretApprovalPolicyBypasser = "secret_approval_policies_bypassers",
|
||||||
SecretApprovalRequest = "secret_approval_requests",
|
SecretApprovalRequest = "secret_approval_requests",
|
||||||
SecretApprovalRequestReviewer = "secret_approval_requests_reviewers",
|
SecretApprovalRequestReviewer = "secret_approval_requests_reviewers",
|
||||||
SecretApprovalRequestSecret = "secret_approval_requests_secrets",
|
SecretApprovalRequestSecret = "secret_approval_requests_secrets",
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ export const OrganizationsSchema = z.object({
|
|||||||
kmsProductEnabled: z.boolean().default(true).nullable().optional(),
|
kmsProductEnabled: z.boolean().default(true).nullable().optional(),
|
||||||
sshProductEnabled: z.boolean().default(true).nullable().optional(),
|
sshProductEnabled: z.boolean().default(true).nullable().optional(),
|
||||||
scannerProductEnabled: z.boolean().default(true).nullable().optional(),
|
scannerProductEnabled: z.boolean().default(true).nullable().optional(),
|
||||||
shareSecretsProductEnabled: z.boolean().default(true).nullable().optional()
|
shareSecretsProductEnabled: z.boolean().default(true).nullable().optional(),
|
||||||
|
maxSharedSecretLifetime: z.number().default(2592000).nullable().optional(),
|
||||||
|
maxSharedSecretViewLimit: z.number().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TOrganizations = z.infer<typeof OrganizationsSchema>;
|
export type TOrganizations = z.infer<typeof OrganizationsSchema>;
|
||||||
|
|||||||
@@ -16,10 +16,16 @@ export const PkiSubscribersSchema = z.object({
|
|||||||
name: z.string(),
|
name: z.string(),
|
||||||
commonName: z.string(),
|
commonName: z.string(),
|
||||||
subjectAlternativeNames: z.string().array(),
|
subjectAlternativeNames: z.string().array(),
|
||||||
ttl: z.string(),
|
ttl: z.string().nullable().optional(),
|
||||||
keyUsages: z.string().array(),
|
keyUsages: z.string().array(),
|
||||||
extendedKeyUsages: z.string().array(),
|
extendedKeyUsages: z.string().array(),
|
||||||
status: z.string()
|
status: z.string(),
|
||||||
|
enableAutoRenewal: z.boolean().default(false),
|
||||||
|
autoRenewalPeriodInDays: z.number().nullable().optional(),
|
||||||
|
lastAutoRenewAt: z.date().nullable().optional(),
|
||||||
|
lastOperationStatus: z.string().nullable().optional(),
|
||||||
|
lastOperationMessage: z.string().nullable().optional(),
|
||||||
|
lastOperationAt: z.date().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TPkiSubscribers = z.infer<typeof PkiSubscribersSchema>;
|
export type TPkiSubscribers = z.infer<typeof PkiSubscribersSchema>;
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
// Code generated by automation script, DO NOT EDIT.
|
||||||
|
// Automated by pulling database and generating zod schema
|
||||||
|
// To update. Just run npm run generate:schema
|
||||||
|
// Written by akhilmhdh.
|
||||||
|
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { TImmutableDBKeys } from "./models";
|
||||||
|
|
||||||
|
export const SecretApprovalPoliciesBypassersSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
bypasserGroupId: z.string().uuid().nullable().optional(),
|
||||||
|
bypasserUserId: z.string().uuid().nullable().optional(),
|
||||||
|
policyId: z.string().uuid(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TSecretApprovalPoliciesBypassers = z.infer<typeof SecretApprovalPoliciesBypassersSchema>;
|
||||||
|
export type TSecretApprovalPoliciesBypassersInsert = Omit<
|
||||||
|
z.input<typeof SecretApprovalPoliciesBypassersSchema>,
|
||||||
|
TImmutableDBKeys
|
||||||
|
>;
|
||||||
|
export type TSecretApprovalPoliciesBypassersUpdate = Partial<
|
||||||
|
Omit<z.input<typeof SecretApprovalPoliciesBypassersSchema>, TImmutableDBKeys>
|
||||||
|
>;
|
||||||
@@ -28,7 +28,6 @@ export const SecretSharingSchema = z.object({
|
|||||||
encryptedSecret: zodBuffer.nullable().optional(),
|
encryptedSecret: zodBuffer.nullable().optional(),
|
||||||
identifier: z.string().nullable().optional(),
|
identifier: z.string().nullable().optional(),
|
||||||
type: z.string().default("share"),
|
type: z.string().default("share"),
|
||||||
encryptedSalt: zodBuffer.nullable().optional(),
|
|
||||||
authorizedEmails: z.unknown().nullable().optional()
|
authorizedEmails: z.unknown().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { ApproverType } from "@app/ee/services/access-approval-policy/access-approval-policy-types";
|
import { ApproverType, BypasserType } from "@app/ee/services/access-approval-policy/access-approval-policy-types";
|
||||||
import { EnforcementLevel } from "@app/lib/types";
|
import { EnforcementLevel } from "@app/lib/types";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
@@ -24,10 +24,19 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
approvers: z
|
approvers: z
|
||||||
.discriminatedUnion("type", [
|
.discriminatedUnion("type", [
|
||||||
z.object({ type: z.literal(ApproverType.Group), id: z.string() }),
|
z.object({ type: z.literal(ApproverType.Group), id: z.string() }),
|
||||||
z.object({ type: z.literal(ApproverType.User), id: z.string().optional(), name: z.string().optional() })
|
z.object({ type: z.literal(ApproverType.User), id: z.string().optional(), username: z.string().optional() })
|
||||||
])
|
])
|
||||||
.array()
|
.array()
|
||||||
|
.max(100, "Cannot have more than 100 approvers")
|
||||||
.min(1, { message: "At least one approver should be provided" }),
|
.min(1, { message: "At least one approver should be provided" }),
|
||||||
|
bypassers: z
|
||||||
|
.discriminatedUnion("type", [
|
||||||
|
z.object({ type: z.literal(BypasserType.Group), id: z.string() }),
|
||||||
|
z.object({ type: z.literal(BypasserType.User), id: z.string().optional(), username: z.string().optional() })
|
||||||
|
])
|
||||||
|
.array()
|
||||||
|
.max(100, "Cannot have more than 100 bypassers")
|
||||||
|
.optional(),
|
||||||
approvals: z.number().min(1).default(1),
|
approvals: z.number().min(1).default(1),
|
||||||
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard),
|
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard),
|
||||||
allowedSelfApprovals: z.boolean().default(true)
|
allowedSelfApprovals: z.boolean().default(true)
|
||||||
@@ -72,7 +81,8 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
.object({ type: z.nativeEnum(ApproverType), id: z.string().nullable().optional() })
|
.object({ type: z.nativeEnum(ApproverType), id: z.string().nullable().optional() })
|
||||||
.array()
|
.array()
|
||||||
.nullable()
|
.nullable()
|
||||||
.optional()
|
.optional(),
|
||||||
|
bypassers: z.object({ type: z.nativeEnum(BypasserType), id: z.string().nullable().optional() }).array()
|
||||||
})
|
})
|
||||||
.array()
|
.array()
|
||||||
.nullable()
|
.nullable()
|
||||||
@@ -143,10 +153,19 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
approvers: z
|
approvers: z
|
||||||
.discriminatedUnion("type", [
|
.discriminatedUnion("type", [
|
||||||
z.object({ type: z.literal(ApproverType.Group), id: z.string() }),
|
z.object({ type: z.literal(ApproverType.Group), id: z.string() }),
|
||||||
z.object({ type: z.literal(ApproverType.User), id: z.string().optional(), name: z.string().optional() })
|
z.object({ type: z.literal(ApproverType.User), id: z.string().optional(), username: z.string().optional() })
|
||||||
])
|
])
|
||||||
.array()
|
.array()
|
||||||
.min(1, { message: "At least one approver should be provided" }),
|
.min(1, { message: "At least one approver should be provided" })
|
||||||
|
.max(100, "Cannot have more than 100 approvers"),
|
||||||
|
bypassers: z
|
||||||
|
.discriminatedUnion("type", [
|
||||||
|
z.object({ type: z.literal(BypasserType.Group), id: z.string() }),
|
||||||
|
z.object({ type: z.literal(BypasserType.User), id: z.string().optional(), username: z.string().optional() })
|
||||||
|
])
|
||||||
|
.array()
|
||||||
|
.max(100, "Cannot have more than 100 bypassers")
|
||||||
|
.optional(),
|
||||||
approvals: z.number().min(1).optional(),
|
approvals: z.number().min(1).optional(),
|
||||||
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard),
|
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard),
|
||||||
allowedSelfApprovals: z.boolean().default(true)
|
allowedSelfApprovals: z.boolean().default(true)
|
||||||
@@ -220,6 +239,15 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
})
|
})
|
||||||
.array()
|
.array()
|
||||||
.nullable()
|
.nullable()
|
||||||
|
.optional(),
|
||||||
|
bypassers: z
|
||||||
|
.object({
|
||||||
|
type: z.nativeEnum(BypasserType),
|
||||||
|
id: z.string().nullable().optional(),
|
||||||
|
name: z.string().nullable().optional()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
.nullable()
|
||||||
.optional()
|
.optional()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
name: z.string(),
|
name: z.string(),
|
||||||
approvals: z.number(),
|
approvals: z.number(),
|
||||||
approvers: z.string().array(),
|
approvers: z.string().array(),
|
||||||
|
bypassers: z.string().array(),
|
||||||
secretPath: z.string().nullish(),
|
secretPath: z.string().nullish(),
|
||||||
envId: z.string(),
|
envId: z.string(),
|
||||||
enforcementLevel: z.string(),
|
enforcementLevel: z.string(),
|
||||||
@@ -154,7 +155,8 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
requestId: z.string().trim()
|
requestId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
status: z.enum([ApprovalStatus.APPROVED, ApprovalStatus.REJECTED])
|
status: z.enum([ApprovalStatus.APPROVED, ApprovalStatus.REJECTED]),
|
||||||
|
bypassReason: z.string().min(10).max(1000).optional()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@@ -170,7 +172,8 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
requestId: req.params.requestId,
|
requestId: req.params.requestId,
|
||||||
status: req.body.status
|
status: req.body.status,
|
||||||
|
bypassReason: req.body.bypassReason
|
||||||
});
|
});
|
||||||
|
|
||||||
return { review };
|
return { review };
|
||||||
|
|||||||
+5
-5
@@ -1,16 +1,16 @@
|
|||||||
import z from "zod";
|
import z from "zod";
|
||||||
|
|
||||||
import { readLimit } from "@app/server/config/rateLimiter";
|
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
|
||||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
|
||||||
import {
|
import {
|
||||||
CreateOCIConnectionSchema,
|
CreateOCIConnectionSchema,
|
||||||
SanitizedOCIConnectionSchema,
|
SanitizedOCIConnectionSchema,
|
||||||
UpdateOCIConnectionSchema
|
UpdateOCIConnectionSchema
|
||||||
} from "@app/services/app-connection/oci";
|
} from "@app/ee/services/app-connections/oci";
|
||||||
|
import { readLimit } from "@app/server/config/rateLimiter";
|
||||||
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
import { registerAppConnectionEndpoints } from "./app-connection-endpoints";
|
import { registerAppConnectionEndpoints } from "../../../../server/routes/v1/app-connection-routers/app-connection-endpoints";
|
||||||
|
|
||||||
export const registerOCIConnectionRouter = async (server: FastifyZodProvider) => {
|
export const registerOCIConnectionRouter = async (server: FastifyZodProvider) => {
|
||||||
registerAppConnectionEndpoints({
|
registerAppConnectionEndpoints({
|
||||||
@@ -6,6 +6,8 @@ import { ApiDocsTags, DYNAMIC_SECRETS } from "@app/lib/api-docs";
|
|||||||
import { daysToMillisecond } from "@app/lib/dates";
|
import { daysToMillisecond } from "@app/lib/dates";
|
||||||
import { removeTrailingSlash } from "@app/lib/fn";
|
import { removeTrailingSlash } from "@app/lib/fn";
|
||||||
import { ms } from "@app/lib/ms";
|
import { ms } from "@app/lib/ms";
|
||||||
|
import { isValidHandleBarTemplate } from "@app/lib/template/validate-handlebars";
|
||||||
|
import { CharacterType, characterValidator } from "@app/lib/validator/validate-string";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { slugSchema } from "@app/server/lib/schemas";
|
import { slugSchema } from "@app/server/lib/schemas";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
@@ -13,6 +15,28 @@ import { SanitizedDynamicSecretSchema } from "@app/server/routes/sanitizedSchema
|
|||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
import { ResourceMetadataSchema } from "@app/services/resource-metadata/resource-metadata-schema";
|
import { ResourceMetadataSchema } from "@app/services/resource-metadata/resource-metadata-schema";
|
||||||
|
|
||||||
|
const validateUsernameTemplateCharacters = characterValidator([
|
||||||
|
CharacterType.AlphaNumeric,
|
||||||
|
CharacterType.Underscore,
|
||||||
|
CharacterType.Hyphen,
|
||||||
|
CharacterType.OpenBrace,
|
||||||
|
CharacterType.CloseBrace,
|
||||||
|
CharacterType.CloseBracket,
|
||||||
|
CharacterType.OpenBracket,
|
||||||
|
CharacterType.Fullstop
|
||||||
|
]);
|
||||||
|
|
||||||
|
const userTemplateSchema = z
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.max(255)
|
||||||
|
.refine((el) => validateUsernameTemplateCharacters(el))
|
||||||
|
.refine((el) =>
|
||||||
|
isValidHandleBarTemplate(el, {
|
||||||
|
allowedExpressions: (val) => ["randomUsername", "unixTimestamp"].includes(val)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => {
|
export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@@ -52,7 +76,8 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
|
|||||||
path: z.string().describe(DYNAMIC_SECRETS.CREATE.path).trim().default("/").transform(removeTrailingSlash),
|
path: z.string().describe(DYNAMIC_SECRETS.CREATE.path).trim().default("/").transform(removeTrailingSlash),
|
||||||
environmentSlug: z.string().describe(DYNAMIC_SECRETS.CREATE.environmentSlug).min(1),
|
environmentSlug: z.string().describe(DYNAMIC_SECRETS.CREATE.environmentSlug).min(1),
|
||||||
name: slugSchema({ min: 1, max: 64, field: "Name" }).describe(DYNAMIC_SECRETS.CREATE.name),
|
name: slugSchema({ min: 1, max: 64, field: "Name" }).describe(DYNAMIC_SECRETS.CREATE.name),
|
||||||
metadata: ResourceMetadataSchema.optional()
|
metadata: ResourceMetadataSchema.optional(),
|
||||||
|
usernameTemplate: userTemplateSchema.optional()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@@ -73,39 +98,6 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
|
||||||
method: "POST",
|
|
||||||
url: "/entra-id/users",
|
|
||||||
config: {
|
|
||||||
rateLimit: readLimit
|
|
||||||
},
|
|
||||||
schema: {
|
|
||||||
body: z.object({
|
|
||||||
tenantId: z.string().min(1).describe("The tenant ID of the Azure Entra ID"),
|
|
||||||
applicationId: z.string().min(1).describe("The application ID of the Azure Entra ID App Registration"),
|
|
||||||
clientSecret: z.string().min(1).describe("The client secret of the Azure Entra ID App Registration")
|
|
||||||
}),
|
|
||||||
response: {
|
|
||||||
200: z
|
|
||||||
.object({
|
|
||||||
name: z.string().min(1).describe("The name of the user"),
|
|
||||||
id: z.string().min(1).describe("The ID of the user"),
|
|
||||||
email: z.string().min(1).describe("The email of the user")
|
|
||||||
})
|
|
||||||
.array()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
|
||||||
handler: async (req) => {
|
|
||||||
const data = await server.services.dynamicSecret.fetchAzureEntraIdUsers({
|
|
||||||
tenantId: req.body.tenantId,
|
|
||||||
applicationId: req.body.applicationId,
|
|
||||||
clientSecret: req.body.clientSecret
|
|
||||||
});
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
url: "/:name",
|
url: "/:name",
|
||||||
@@ -150,7 +142,8 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
|
|||||||
})
|
})
|
||||||
.nullable(),
|
.nullable(),
|
||||||
newName: z.string().describe(DYNAMIC_SECRETS.UPDATE.newName).optional(),
|
newName: z.string().describe(DYNAMIC_SECRETS.UPDATE.newName).optional(),
|
||||||
metadata: ResourceMetadataSchema.optional()
|
metadata: ResourceMetadataSchema.optional(),
|
||||||
|
usernameTemplate: userTemplateSchema.nullable().optional()
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
@@ -328,4 +321,37 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
|
|||||||
return { leases };
|
return { leases };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/entra-id/users",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
body: z.object({
|
||||||
|
tenantId: z.string().min(1).describe("The tenant ID of the Azure Entra ID"),
|
||||||
|
applicationId: z.string().min(1).describe("The application ID of the Azure Entra ID App Registration"),
|
||||||
|
clientSecret: z.string().min(1).describe("The client secret of the Azure Entra ID App Registration")
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z
|
||||||
|
.object({
|
||||||
|
name: z.string().min(1).describe("The name of the user"),
|
||||||
|
id: z.string().min(1).describe("The ID of the user"),
|
||||||
|
email: z.string().min(1).describe("The email of the user")
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const data = await server.services.dynamicSecret.fetchAzureEntraIdUsers({
|
||||||
|
tenantId: req.body.tenantId,
|
||||||
|
applicationId: req.body.applicationId,
|
||||||
|
clientSecret: req.body.clientSecret
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
|||||||
200: z.object({ plan: z.any() })
|
200: z.object({ plan: z.any() })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const plan = await server.services.license.getOrgPlan({
|
const plan = await server.services.license.getOrgPlan({
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
|||||||
|
|
||||||
const { isUserCompleted, providerAuthToken } = await server.services.saml.samlLogin({
|
const { isUserCompleted, providerAuthToken } = await server.services.saml.samlLogin({
|
||||||
externalId: profile.nameID,
|
externalId: profile.nameID,
|
||||||
email,
|
email: email.toLowerCase(),
|
||||||
firstName,
|
firstName,
|
||||||
lastName: lastName as string,
|
lastName: lastName as string,
|
||||||
relayState: (req.body as { RelayState?: string }).RelayState,
|
relayState: (req.body as { RelayState?: string }).RelayState,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { ApproverType } from "@app/ee/services/access-approval-policy/access-approval-policy-types";
|
import { ApproverType, BypasserType } from "@app/ee/services/access-approval-policy/access-approval-policy-types";
|
||||||
import { removeTrailingSlash } from "@app/lib/fn";
|
import { removeTrailingSlash } from "@app/lib/fn";
|
||||||
import { EnforcementLevel } from "@app/lib/types";
|
import { EnforcementLevel } from "@app/lib/types";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
@@ -30,10 +30,19 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
approvers: z
|
approvers: z
|
||||||
.discriminatedUnion("type", [
|
.discriminatedUnion("type", [
|
||||||
z.object({ type: z.literal(ApproverType.Group), id: z.string() }),
|
z.object({ type: z.literal(ApproverType.Group), id: z.string() }),
|
||||||
z.object({ type: z.literal(ApproverType.User), id: z.string().optional(), name: z.string().optional() })
|
z.object({ type: z.literal(ApproverType.User), id: z.string().optional(), username: z.string().optional() })
|
||||||
])
|
])
|
||||||
.array()
|
.array()
|
||||||
.min(1, { message: "At least one approver should be provided" }),
|
.min(1, { message: "At least one approver should be provided" })
|
||||||
|
.max(100, "Cannot have more than 100 approvers"),
|
||||||
|
bypassers: z
|
||||||
|
.discriminatedUnion("type", [
|
||||||
|
z.object({ type: z.literal(BypasserType.Group), id: z.string() }),
|
||||||
|
z.object({ type: z.literal(BypasserType.User), id: z.string().optional(), username: z.string().optional() })
|
||||||
|
])
|
||||||
|
.array()
|
||||||
|
.max(100, "Cannot have more than 100 bypassers")
|
||||||
|
.optional(),
|
||||||
approvals: z.number().min(1).default(1),
|
approvals: z.number().min(1).default(1),
|
||||||
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard),
|
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard),
|
||||||
allowedSelfApprovals: z.boolean().default(true)
|
allowedSelfApprovals: z.boolean().default(true)
|
||||||
@@ -75,10 +84,19 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
approvers: z
|
approvers: z
|
||||||
.discriminatedUnion("type", [
|
.discriminatedUnion("type", [
|
||||||
z.object({ type: z.literal(ApproverType.Group), id: z.string() }),
|
z.object({ type: z.literal(ApproverType.Group), id: z.string() }),
|
||||||
z.object({ type: z.literal(ApproverType.User), id: z.string().optional(), name: z.string().optional() })
|
z.object({ type: z.literal(ApproverType.User), id: z.string().optional(), username: z.string().optional() })
|
||||||
])
|
])
|
||||||
.array()
|
.array()
|
||||||
.min(1, { message: "At least one approver should be provided" }),
|
.min(1, { message: "At least one approver should be provided" })
|
||||||
|
.max(100, "Cannot have more than 100 approvers"),
|
||||||
|
bypassers: z
|
||||||
|
.discriminatedUnion("type", [
|
||||||
|
z.object({ type: z.literal(BypasserType.Group), id: z.string() }),
|
||||||
|
z.object({ type: z.literal(BypasserType.User), id: z.string().optional(), username: z.string().optional() })
|
||||||
|
])
|
||||||
|
.array()
|
||||||
|
.max(100, "Cannot have more than 100 bypassers")
|
||||||
|
.optional(),
|
||||||
approvals: z.number().min(1).default(1),
|
approvals: z.number().min(1).default(1),
|
||||||
secretPath: z
|
secretPath: z
|
||||||
.string()
|
.string()
|
||||||
@@ -157,6 +175,12 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
id: z.string().nullable().optional(),
|
id: z.string().nullable().optional(),
|
||||||
type: z.nativeEnum(ApproverType)
|
type: z.nativeEnum(ApproverType)
|
||||||
})
|
})
|
||||||
|
.array(),
|
||||||
|
bypassers: z
|
||||||
|
.object({
|
||||||
|
id: z.string().nullable().optional(),
|
||||||
|
type: z.nativeEnum(BypasserType)
|
||||||
|
})
|
||||||
.array()
|
.array()
|
||||||
})
|
})
|
||||||
.array()
|
.array()
|
||||||
@@ -193,7 +217,14 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
.object({
|
.object({
|
||||||
id: z.string().nullable().optional(),
|
id: z.string().nullable().optional(),
|
||||||
type: z.nativeEnum(ApproverType),
|
type: z.nativeEnum(ApproverType),
|
||||||
name: z.string().nullable().optional()
|
username: z.string().nullable().optional()
|
||||||
|
})
|
||||||
|
.array(),
|
||||||
|
bypassers: z
|
||||||
|
.object({
|
||||||
|
id: z.string().nullable().optional(),
|
||||||
|
type: z.nativeEnum(BypasserType),
|
||||||
|
username: z.string().nullable().optional()
|
||||||
})
|
})
|
||||||
.array()
|
.array()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -47,6 +47,11 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
userId: z.string().nullable().optional()
|
userId: z.string().nullable().optional()
|
||||||
})
|
})
|
||||||
.array(),
|
.array(),
|
||||||
|
bypassers: z
|
||||||
|
.object({
|
||||||
|
userId: z.string().nullable().optional()
|
||||||
|
})
|
||||||
|
.array(),
|
||||||
secretPath: z.string().optional().nullable(),
|
secretPath: z.string().optional().nullable(),
|
||||||
enforcementLevel: z.string(),
|
enforcementLevel: z.string(),
|
||||||
deletedAt: z.date().nullish(),
|
deletedAt: z.date().nullish(),
|
||||||
@@ -266,6 +271,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
name: z.string(),
|
name: z.string(),
|
||||||
approvals: z.number(),
|
approvals: z.number(),
|
||||||
approvers: approvalRequestUser.array(),
|
approvers: approvalRequestUser.array(),
|
||||||
|
bypassers: approvalRequestUser.array(),
|
||||||
secretPath: z.string().optional().nullable(),
|
secretPath: z.string().optional().nullable(),
|
||||||
enforcementLevel: z.string(),
|
enforcementLevel: z.string(),
|
||||||
deletedAt: z.date().nullish(),
|
deletedAt: z.date().nullish(),
|
||||||
|
|||||||
+2
-3
@@ -2,11 +2,10 @@ import {
|
|||||||
CreateOCIVaultSyncSchema,
|
CreateOCIVaultSyncSchema,
|
||||||
OCIVaultSyncSchema,
|
OCIVaultSyncSchema,
|
||||||
UpdateOCIVaultSyncSchema
|
UpdateOCIVaultSyncSchema
|
||||||
} from "@app/services/secret-sync/oci-vault";
|
} from "@app/ee/services/secret-sync/oci-vault";
|
||||||
|
import { registerSyncSecretsEndpoints } from "@app/server/routes/v1/secret-sync-routers/secret-sync-endpoints";
|
||||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||||
|
|
||||||
import { registerSyncSecretsEndpoints } from "./secret-sync-endpoints";
|
|
||||||
|
|
||||||
export const registerOCIVaultSyncRouter = async (server: FastifyZodProvider) =>
|
export const registerOCIVaultSyncRouter = async (server: FastifyZodProvider) =>
|
||||||
registerSyncSecretsEndpoints({
|
registerSyncSecretsEndpoints({
|
||||||
destination: SecretSync.OCIVault,
|
destination: SecretSync.OCIVault,
|
||||||
@@ -5,6 +5,7 @@ import { registerAwsIamUserSecretRotationRouter } from "./aws-iam-user-secret-ro
|
|||||||
import { registerAzureClientSecretRotationRouter } from "./azure-client-secret-rotation-router";
|
import { registerAzureClientSecretRotationRouter } from "./azure-client-secret-rotation-router";
|
||||||
import { registerLdapPasswordRotationRouter } from "./ldap-password-rotation-router";
|
import { registerLdapPasswordRotationRouter } from "./ldap-password-rotation-router";
|
||||||
import { registerMsSqlCredentialsRotationRouter } from "./mssql-credentials-rotation-router";
|
import { registerMsSqlCredentialsRotationRouter } from "./mssql-credentials-rotation-router";
|
||||||
|
import { registerMySqlCredentialsRotationRouter } from "./mysql-credentials-rotation-router";
|
||||||
import { registerPostgresCredentialsRotationRouter } from "./postgres-credentials-rotation-router";
|
import { registerPostgresCredentialsRotationRouter } from "./postgres-credentials-rotation-router";
|
||||||
|
|
||||||
export * from "./secret-rotation-v2-router";
|
export * from "./secret-rotation-v2-router";
|
||||||
@@ -15,6 +16,7 @@ export const SECRET_ROTATION_REGISTER_ROUTER_MAP: Record<
|
|||||||
> = {
|
> = {
|
||||||
[SecretRotation.PostgresCredentials]: registerPostgresCredentialsRotationRouter,
|
[SecretRotation.PostgresCredentials]: registerPostgresCredentialsRotationRouter,
|
||||||
[SecretRotation.MsSqlCredentials]: registerMsSqlCredentialsRotationRouter,
|
[SecretRotation.MsSqlCredentials]: registerMsSqlCredentialsRotationRouter,
|
||||||
|
[SecretRotation.MySqlCredentials]: registerMySqlCredentialsRotationRouter,
|
||||||
[SecretRotation.Auth0ClientSecret]: registerAuth0ClientSecretRotationRouter,
|
[SecretRotation.Auth0ClientSecret]: registerAuth0ClientSecretRotationRouter,
|
||||||
[SecretRotation.AzureClientSecret]: registerAzureClientSecretRotationRouter,
|
[SecretRotation.AzureClientSecret]: registerAzureClientSecretRotationRouter,
|
||||||
[SecretRotation.AwsIamUserSecret]: registerAwsIamUserSecretRotationRouter,
|
[SecretRotation.AwsIamUserSecret]: registerAwsIamUserSecretRotationRouter,
|
||||||
|
|||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
import {
|
||||||
|
CreateMySqlCredentialsRotationSchema,
|
||||||
|
MySqlCredentialsRotationSchema,
|
||||||
|
UpdateMySqlCredentialsRotationSchema
|
||||||
|
} from "@app/ee/services/secret-rotation-v2/mysql-credentials";
|
||||||
|
import { SecretRotation } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-enums";
|
||||||
|
import { SqlCredentialsRotationGeneratedCredentialsSchema } from "@app/ee/services/secret-rotation-v2/shared/sql-credentials";
|
||||||
|
|
||||||
|
import { registerSecretRotationEndpoints } from "./secret-rotation-v2-endpoints";
|
||||||
|
|
||||||
|
export const registerMySqlCredentialsRotationRouter = async (server: FastifyZodProvider) =>
|
||||||
|
registerSecretRotationEndpoints({
|
||||||
|
type: SecretRotation.MySqlCredentials,
|
||||||
|
server,
|
||||||
|
responseSchema: MySqlCredentialsRotationSchema,
|
||||||
|
createSchema: CreateMySqlCredentialsRotationSchema,
|
||||||
|
updateSchema: UpdateMySqlCredentialsRotationSchema,
|
||||||
|
generatedCredentialsSchema: SqlCredentialsRotationGeneratedCredentialsSchema
|
||||||
|
});
|
||||||
@@ -6,6 +6,7 @@ import { AwsIamUserSecretRotationListItemSchema } from "@app/ee/services/secret-
|
|||||||
import { AzureClientSecretRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/azure-client-secret";
|
import { AzureClientSecretRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/azure-client-secret";
|
||||||
import { LdapPasswordRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/ldap-password";
|
import { LdapPasswordRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/ldap-password";
|
||||||
import { MsSqlCredentialsRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/mssql-credentials";
|
import { MsSqlCredentialsRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/mssql-credentials";
|
||||||
|
import { MySqlCredentialsRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/mysql-credentials";
|
||||||
import { PostgresCredentialsRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/postgres-credentials";
|
import { PostgresCredentialsRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/postgres-credentials";
|
||||||
import { SecretRotationV2Schema } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-union-schema";
|
import { SecretRotationV2Schema } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-union-schema";
|
||||||
import { ApiDocsTags, SecretRotations } from "@app/lib/api-docs";
|
import { ApiDocsTags, SecretRotations } from "@app/lib/api-docs";
|
||||||
@@ -16,6 +17,7 @@ import { AuthMode } from "@app/services/auth/auth-type";
|
|||||||
const SecretRotationV2OptionsSchema = z.discriminatedUnion("type", [
|
const SecretRotationV2OptionsSchema = z.discriminatedUnion("type", [
|
||||||
PostgresCredentialsRotationListItemSchema,
|
PostgresCredentialsRotationListItemSchema,
|
||||||
MsSqlCredentialsRotationListItemSchema,
|
MsSqlCredentialsRotationListItemSchema,
|
||||||
|
MySqlCredentialsRotationListItemSchema,
|
||||||
Auth0ClientSecretRotationListItemSchema,
|
Auth0ClientSecretRotationListItemSchema,
|
||||||
AzureClientSecretRotationListItemSchema,
|
AzureClientSecretRotationListItemSchema,
|
||||||
AwsIamUserSecretRotationListItemSchema,
|
AwsIamUserSecretRotationListItemSchema,
|
||||||
|
|||||||
@@ -8,3 +8,10 @@ export const accessApprovalPolicyApproverDALFactory = (db: TDbClient) => {
|
|||||||
const accessApprovalPolicyApproverOrm = ormify(db, TableName.AccessApprovalPolicyApprover);
|
const accessApprovalPolicyApproverOrm = ormify(db, TableName.AccessApprovalPolicyApprover);
|
||||||
return { ...accessApprovalPolicyApproverOrm };
|
return { ...accessApprovalPolicyApproverOrm };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TAccessApprovalPolicyBypasserDALFactory = ReturnType<typeof accessApprovalPolicyBypasserDALFactory>;
|
||||||
|
|
||||||
|
export const accessApprovalPolicyBypasserDALFactory = (db: TDbClient) => {
|
||||||
|
const accessApprovalPolicyBypasserOrm = ormify(db, TableName.AccessApprovalPolicyBypasser);
|
||||||
|
return { ...accessApprovalPolicyBypasserOrm };
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import { AccessApprovalPoliciesSchema, TableName, TAccessApprovalPolicies } from "@app/db/schemas";
|
import { AccessApprovalPoliciesSchema, TableName, TAccessApprovalPolicies, TUsers } from "@app/db/schemas";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import { buildFindFilter, ormify, selectAllTableCols, sqlNestRelationships, TFindFilter } from "@app/lib/knex";
|
import { buildFindFilter, ormify, selectAllTableCols, sqlNestRelationships, TFindFilter } from "@app/lib/knex";
|
||||||
|
|
||||||
import { ApproverType } from "./access-approval-policy-types";
|
import { ApproverType, BypasserType } from "./access-approval-policy-types";
|
||||||
|
|
||||||
export type TAccessApprovalPolicyDALFactory = ReturnType<typeof accessApprovalPolicyDALFactory>;
|
export type TAccessApprovalPolicyDALFactory = ReturnType<typeof accessApprovalPolicyDALFactory>;
|
||||||
|
|
||||||
@@ -34,9 +34,22 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.AccessApprovalPolicyApprover}.policyId`
|
`${TableName.AccessApprovalPolicyApprover}.policyId`
|
||||||
)
|
)
|
||||||
.leftJoin(TableName.Users, `${TableName.AccessApprovalPolicyApprover}.approverUserId`, `${TableName.Users}.id`)
|
.leftJoin(TableName.Users, `${TableName.AccessApprovalPolicyApprover}.approverUserId`, `${TableName.Users}.id`)
|
||||||
|
.leftJoin(
|
||||||
|
TableName.AccessApprovalPolicyBypasser,
|
||||||
|
`${TableName.AccessApprovalPolicy}.id`,
|
||||||
|
`${TableName.AccessApprovalPolicyBypasser}.policyId`
|
||||||
|
)
|
||||||
|
.leftJoin<TUsers>(
|
||||||
|
db(TableName.Users).as("bypasserUsers"),
|
||||||
|
`${TableName.AccessApprovalPolicyBypasser}.bypasserUserId`,
|
||||||
|
`bypasserUsers.id`
|
||||||
|
)
|
||||||
.select(tx.ref("username").withSchema(TableName.Users).as("approverUsername"))
|
.select(tx.ref("username").withSchema(TableName.Users).as("approverUsername"))
|
||||||
|
.select(tx.ref("username").withSchema("bypasserUsers").as("bypasserUsername"))
|
||||||
.select(tx.ref("approverUserId").withSchema(TableName.AccessApprovalPolicyApprover))
|
.select(tx.ref("approverUserId").withSchema(TableName.AccessApprovalPolicyApprover))
|
||||||
.select(tx.ref("approverGroupId").withSchema(TableName.AccessApprovalPolicyApprover))
|
.select(tx.ref("approverGroupId").withSchema(TableName.AccessApprovalPolicyApprover))
|
||||||
|
.select(tx.ref("bypasserUserId").withSchema(TableName.AccessApprovalPolicyBypasser))
|
||||||
|
.select(tx.ref("bypasserGroupId").withSchema(TableName.AccessApprovalPolicyBypasser))
|
||||||
.select(tx.ref("name").withSchema(TableName.Environment).as("envName"))
|
.select(tx.ref("name").withSchema(TableName.Environment).as("envName"))
|
||||||
.select(tx.ref("slug").withSchema(TableName.Environment).as("envSlug"))
|
.select(tx.ref("slug").withSchema(TableName.Environment).as("envSlug"))
|
||||||
.select(tx.ref("id").withSchema(TableName.Environment).as("envId"))
|
.select(tx.ref("id").withSchema(TableName.Environment).as("envId"))
|
||||||
@@ -129,6 +142,23 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => {
|
|||||||
id,
|
id,
|
||||||
type: ApproverType.Group
|
type: ApproverType.Group
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "bypasserUserId",
|
||||||
|
label: "bypassers" as const,
|
||||||
|
mapper: ({ bypasserUserId: id, bypasserUsername }) => ({
|
||||||
|
id,
|
||||||
|
type: BypasserType.User,
|
||||||
|
name: bypasserUsername
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "bypasserGroupId",
|
||||||
|
label: "bypassers" as const,
|
||||||
|
mapper: ({ bypasserGroupId: id }) => ({
|
||||||
|
id,
|
||||||
|
type: BypasserType.Group
|
||||||
|
})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
@@ -144,5 +174,28 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => {
|
|||||||
return softDeletedPolicy;
|
return softDeletedPolicy;
|
||||||
};
|
};
|
||||||
|
|
||||||
return { ...accessApprovalPolicyOrm, find, findById, softDeleteById };
|
const findLastValidPolicy = async ({ envId, secretPath }: { envId: string; secretPath: string }, tx?: Knex) => {
|
||||||
|
try {
|
||||||
|
const result = await (tx || db.replicaNode())(TableName.AccessApprovalPolicy)
|
||||||
|
.where(
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
|
buildFindFilter(
|
||||||
|
{
|
||||||
|
envId,
|
||||||
|
secretPath
|
||||||
|
},
|
||||||
|
TableName.AccessApprovalPolicy
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.orderBy("deletedAt", "desc")
|
||||||
|
.orderByRaw(`"deletedAt" IS NULL`)
|
||||||
|
.first();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "FindLastValidPolicy" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return { ...accessApprovalPolicyOrm, find, findById, softDeleteById, findLastValidPolicy };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { ActionProjectType } from "@app/db/schemas";
|
|||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
|
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
|
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
|
||||||
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
||||||
@@ -14,10 +15,14 @@ import { TAccessApprovalRequestReviewerDALFactory } from "../access-approval-req
|
|||||||
import { ApprovalStatus } from "../access-approval-request/access-approval-request-types";
|
import { ApprovalStatus } from "../access-approval-request/access-approval-request-types";
|
||||||
import { TGroupDALFactory } from "../group/group-dal";
|
import { TGroupDALFactory } from "../group/group-dal";
|
||||||
import { TProjectUserAdditionalPrivilegeDALFactory } from "../project-user-additional-privilege/project-user-additional-privilege-dal";
|
import { TProjectUserAdditionalPrivilegeDALFactory } from "../project-user-additional-privilege/project-user-additional-privilege-dal";
|
||||||
import { TAccessApprovalPolicyApproverDALFactory } from "./access-approval-policy-approver-dal";
|
import {
|
||||||
|
TAccessApprovalPolicyApproverDALFactory,
|
||||||
|
TAccessApprovalPolicyBypasserDALFactory
|
||||||
|
} from "./access-approval-policy-approver-dal";
|
||||||
import { TAccessApprovalPolicyDALFactory } from "./access-approval-policy-dal";
|
import { TAccessApprovalPolicyDALFactory } from "./access-approval-policy-dal";
|
||||||
import {
|
import {
|
||||||
ApproverType,
|
ApproverType,
|
||||||
|
BypasserType,
|
||||||
TCreateAccessApprovalPolicy,
|
TCreateAccessApprovalPolicy,
|
||||||
TDeleteAccessApprovalPolicy,
|
TDeleteAccessApprovalPolicy,
|
||||||
TGetAccessApprovalPolicyByIdDTO,
|
TGetAccessApprovalPolicyByIdDTO,
|
||||||
@@ -32,12 +37,14 @@ type TAccessApprovalPolicyServiceFactoryDep = {
|
|||||||
accessApprovalPolicyDAL: TAccessApprovalPolicyDALFactory;
|
accessApprovalPolicyDAL: TAccessApprovalPolicyDALFactory;
|
||||||
projectEnvDAL: Pick<TProjectEnvDALFactory, "find" | "findOne">;
|
projectEnvDAL: Pick<TProjectEnvDALFactory, "find" | "findOne">;
|
||||||
accessApprovalPolicyApproverDAL: TAccessApprovalPolicyApproverDALFactory;
|
accessApprovalPolicyApproverDAL: TAccessApprovalPolicyApproverDALFactory;
|
||||||
|
accessApprovalPolicyBypasserDAL: TAccessApprovalPolicyBypasserDALFactory;
|
||||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find">;
|
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find">;
|
||||||
groupDAL: TGroupDALFactory;
|
groupDAL: TGroupDALFactory;
|
||||||
userDAL: Pick<TUserDALFactory, "find">;
|
userDAL: Pick<TUserDALFactory, "find">;
|
||||||
accessApprovalRequestDAL: Pick<TAccessApprovalRequestDALFactory, "update" | "find">;
|
accessApprovalRequestDAL: Pick<TAccessApprovalRequestDALFactory, "update" | "find">;
|
||||||
additionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "delete">;
|
additionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "delete">;
|
||||||
accessApprovalRequestReviewerDAL: Pick<TAccessApprovalRequestReviewerDALFactory, "update">;
|
accessApprovalRequestReviewerDAL: Pick<TAccessApprovalRequestReviewerDALFactory, "update">;
|
||||||
|
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "find">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TAccessApprovalPolicyServiceFactory = ReturnType<typeof accessApprovalPolicyServiceFactory>;
|
export type TAccessApprovalPolicyServiceFactory = ReturnType<typeof accessApprovalPolicyServiceFactory>;
|
||||||
@@ -45,6 +52,7 @@ export type TAccessApprovalPolicyServiceFactory = ReturnType<typeof accessApprov
|
|||||||
export const accessApprovalPolicyServiceFactory = ({
|
export const accessApprovalPolicyServiceFactory = ({
|
||||||
accessApprovalPolicyDAL,
|
accessApprovalPolicyDAL,
|
||||||
accessApprovalPolicyApproverDAL,
|
accessApprovalPolicyApproverDAL,
|
||||||
|
accessApprovalPolicyBypasserDAL,
|
||||||
groupDAL,
|
groupDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
projectEnvDAL,
|
projectEnvDAL,
|
||||||
@@ -52,7 +60,8 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
userDAL,
|
userDAL,
|
||||||
accessApprovalRequestDAL,
|
accessApprovalRequestDAL,
|
||||||
additionalPrivilegeDAL,
|
additionalPrivilegeDAL,
|
||||||
accessApprovalRequestReviewerDAL
|
accessApprovalRequestReviewerDAL,
|
||||||
|
orgMembershipDAL
|
||||||
}: TAccessApprovalPolicyServiceFactoryDep) => {
|
}: TAccessApprovalPolicyServiceFactoryDep) => {
|
||||||
const createAccessApprovalPolicy = async ({
|
const createAccessApprovalPolicy = async ({
|
||||||
name,
|
name,
|
||||||
@@ -63,6 +72,7 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
approvals,
|
approvals,
|
||||||
approvers,
|
approvers,
|
||||||
|
bypassers,
|
||||||
projectSlug,
|
projectSlug,
|
||||||
environment,
|
environment,
|
||||||
enforcementLevel,
|
enforcementLevel,
|
||||||
@@ -82,7 +92,7 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
.filter(Boolean) as string[];
|
.filter(Boolean) as string[];
|
||||||
|
|
||||||
const userApproverNames = approvers
|
const userApproverNames = approvers
|
||||||
.map((approver) => (approver.type === ApproverType.User ? approver.name : undefined))
|
.map((approver) => (approver.type === ApproverType.User ? approver.username : undefined))
|
||||||
.filter(Boolean) as string[];
|
.filter(Boolean) as string[];
|
||||||
|
|
||||||
if (!groupApprovers && approvals > userApprovers.length + userApproverNames.length)
|
if (!groupApprovers && approvals > userApprovers.length + userApproverNames.length)
|
||||||
@@ -147,6 +157,44 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
.map((user) => user.id);
|
.map((user) => user.id);
|
||||||
verifyAllApprovers.push(...verifyGroupApprovers);
|
verifyAllApprovers.push(...verifyGroupApprovers);
|
||||||
|
|
||||||
|
let groupBypassers: string[] = [];
|
||||||
|
let bypasserUserIds: string[] = [];
|
||||||
|
|
||||||
|
if (bypassers && bypassers.length) {
|
||||||
|
groupBypassers = bypassers
|
||||||
|
.filter((bypasser) => bypasser.type === BypasserType.Group)
|
||||||
|
.map((bypasser) => bypasser.id) as string[];
|
||||||
|
|
||||||
|
const userBypassers = bypassers
|
||||||
|
.filter((bypasser) => bypasser.type === BypasserType.User)
|
||||||
|
.map((bypasser) => bypasser.id)
|
||||||
|
.filter(Boolean) as string[];
|
||||||
|
|
||||||
|
const userBypasserNames = bypassers
|
||||||
|
.map((bypasser) => (bypasser.type === BypasserType.User ? bypasser.username : undefined))
|
||||||
|
.filter(Boolean) as string[];
|
||||||
|
|
||||||
|
bypasserUserIds = userBypassers;
|
||||||
|
if (userBypasserNames.length) {
|
||||||
|
const bypasserUsers = await userDAL.find({
|
||||||
|
$in: {
|
||||||
|
username: userBypasserNames
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const bypasserNamesFromDb = bypasserUsers.map((user) => user.username);
|
||||||
|
const invalidUsernames = userBypasserNames.filter((username) => !bypasserNamesFromDb.includes(username));
|
||||||
|
|
||||||
|
if (invalidUsernames.length) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `Invalid bypasser user: ${invalidUsernames.join(", ")}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bypasserUserIds = bypasserUserIds.concat(bypasserUsers.map((user) => user.id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const accessApproval = await accessApprovalPolicyDAL.transaction(async (tx) => {
|
const accessApproval = await accessApprovalPolicyDAL.transaction(async (tx) => {
|
||||||
const doc = await accessApprovalPolicyDAL.create(
|
const doc = await accessApprovalPolicyDAL.create(
|
||||||
{
|
{
|
||||||
@@ -159,6 +207,7 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
if (approverUserIds.length) {
|
if (approverUserIds.length) {
|
||||||
await accessApprovalPolicyApproverDAL.insertMany(
|
await accessApprovalPolicyApproverDAL.insertMany(
|
||||||
approverUserIds.map((userId) => ({
|
approverUserIds.map((userId) => ({
|
||||||
@@ -179,8 +228,29 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bypasserUserIds.length) {
|
||||||
|
await accessApprovalPolicyBypasserDAL.insertMany(
|
||||||
|
bypasserUserIds.map((userId) => ({
|
||||||
|
bypasserUserId: userId,
|
||||||
|
policyId: doc.id
|
||||||
|
})),
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupBypassers.length) {
|
||||||
|
await accessApprovalPolicyBypasserDAL.insertMany(
|
||||||
|
groupBypassers.map((groupId) => ({
|
||||||
|
bypasserGroupId: groupId,
|
||||||
|
policyId: doc.id
|
||||||
|
})),
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return doc;
|
return doc;
|
||||||
});
|
});
|
||||||
|
|
||||||
return { ...accessApproval, environment: env, projectId: project.id };
|
return { ...accessApproval, environment: env, projectId: project.id };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -211,6 +281,7 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
const updateAccessApprovalPolicy = async ({
|
const updateAccessApprovalPolicy = async ({
|
||||||
policyId,
|
policyId,
|
||||||
approvers,
|
approvers,
|
||||||
|
bypassers,
|
||||||
secretPath,
|
secretPath,
|
||||||
name,
|
name,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -231,15 +302,15 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
.filter(Boolean) as string[];
|
.filter(Boolean) as string[];
|
||||||
|
|
||||||
const userApproverNames = approvers
|
const userApproverNames = approvers
|
||||||
.map((approver) => (approver.type === ApproverType.User ? approver.name : undefined))
|
.map((approver) => (approver.type === ApproverType.User ? approver.username : undefined))
|
||||||
.filter(Boolean) as string[];
|
.filter(Boolean) as string[];
|
||||||
|
|
||||||
const accessApprovalPolicy = await accessApprovalPolicyDAL.findById(policyId);
|
const accessApprovalPolicy = await accessApprovalPolicyDAL.findById(policyId);
|
||||||
const currentAppovals = approvals || accessApprovalPolicy.approvals;
|
const currentApprovals = approvals || accessApprovalPolicy.approvals;
|
||||||
if (
|
if (
|
||||||
groupApprovers?.length === 0 &&
|
groupApprovers?.length === 0 &&
|
||||||
userApprovers &&
|
userApprovers &&
|
||||||
currentAppovals > userApprovers.length + userApproverNames.length
|
currentApprovals > userApprovers.length + userApproverNames.length
|
||||||
) {
|
) {
|
||||||
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
|
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
|
||||||
}
|
}
|
||||||
@@ -258,6 +329,78 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval);
|
||||||
|
|
||||||
|
let groupBypassers: string[] = [];
|
||||||
|
let bypasserUserIds: string[] = [];
|
||||||
|
|
||||||
|
if (bypassers && bypassers.length) {
|
||||||
|
groupBypassers = bypassers
|
||||||
|
.filter((bypasser) => bypasser.type === BypasserType.Group)
|
||||||
|
.map((bypasser) => bypasser.id) as string[];
|
||||||
|
|
||||||
|
groupBypassers = [...new Set(groupBypassers)];
|
||||||
|
|
||||||
|
const userBypassers = bypassers
|
||||||
|
.filter((bypasser) => bypasser.type === BypasserType.User)
|
||||||
|
.map((bypasser) => bypasser.id)
|
||||||
|
.filter(Boolean) as string[];
|
||||||
|
|
||||||
|
const userBypasserNames = bypassers
|
||||||
|
.map((bypasser) => (bypasser.type === BypasserType.User ? bypasser.username : undefined))
|
||||||
|
.filter(Boolean) as string[];
|
||||||
|
|
||||||
|
bypasserUserIds = userBypassers;
|
||||||
|
if (userBypasserNames.length) {
|
||||||
|
const bypasserUsers = await userDAL.find({
|
||||||
|
$in: {
|
||||||
|
username: userBypasserNames
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const bypasserNamesFromDb = bypasserUsers.map((user) => user.username);
|
||||||
|
const invalidUsernames = userBypasserNames.filter((username) => !bypasserNamesFromDb.includes(username));
|
||||||
|
|
||||||
|
if (invalidUsernames.length) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `Invalid bypasser user: ${invalidUsernames.join(", ")}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bypasserUserIds = [...new Set(bypasserUserIds.concat(bypasserUsers.map((user) => user.id)))];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate user bypassers
|
||||||
|
if (bypasserUserIds.length > 0) {
|
||||||
|
const orgMemberships = await orgMembershipDAL.find({
|
||||||
|
$in: { userId: bypasserUserIds },
|
||||||
|
orgId: actorOrgId
|
||||||
|
});
|
||||||
|
|
||||||
|
if (orgMemberships.length !== bypasserUserIds.length) {
|
||||||
|
const foundUserIdsInOrg = new Set(orgMemberships.map((mem) => mem.userId));
|
||||||
|
const missingUserIds = bypasserUserIds.filter((id) => !foundUserIdsInOrg.has(id));
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `One or more specified bypasser users are not part of the organization or do not exist. Invalid or non-member user IDs: ${missingUserIds.join(", ")}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate group bypassers
|
||||||
|
if (groupBypassers.length > 0) {
|
||||||
|
const orgGroups = await groupDAL.find({
|
||||||
|
$in: { id: groupBypassers },
|
||||||
|
orgId: actorOrgId
|
||||||
|
});
|
||||||
|
|
||||||
|
if (orgGroups.length !== groupBypassers.length) {
|
||||||
|
const foundGroupIdsInOrg = new Set(orgGroups.map((group) => group.id));
|
||||||
|
const missingGroupIds = groupBypassers.filter((id) => !foundGroupIdsInOrg.has(id));
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `One or more specified bypasser groups are not part of the organization or do not exist. Invalid or non-member group IDs: ${missingGroupIds.join(", ")}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const updatedPolicy = await accessApprovalPolicyDAL.transaction(async (tx) => {
|
const updatedPolicy = await accessApprovalPolicyDAL.transaction(async (tx) => {
|
||||||
const doc = await accessApprovalPolicyDAL.updateById(
|
const doc = await accessApprovalPolicyDAL.updateById(
|
||||||
accessApprovalPolicy.id,
|
accessApprovalPolicy.id,
|
||||||
@@ -313,6 +456,28 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await accessApprovalPolicyBypasserDAL.delete({ policyId: doc.id }, tx);
|
||||||
|
|
||||||
|
if (bypasserUserIds.length) {
|
||||||
|
await accessApprovalPolicyBypasserDAL.insertMany(
|
||||||
|
bypasserUserIds.map((userId) => ({
|
||||||
|
bypasserUserId: userId,
|
||||||
|
policyId: doc.id
|
||||||
|
})),
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupBypassers.length) {
|
||||||
|
await accessApprovalPolicyBypasserDAL.insertMany(
|
||||||
|
groupBypassers.map((groupId) => ({
|
||||||
|
bypasserGroupId: groupId,
|
||||||
|
policyId: doc.id
|
||||||
|
})),
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return doc;
|
return doc;
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -18,11 +18,20 @@ export enum ApproverType {
|
|||||||
User = "user"
|
User = "user"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum BypasserType {
|
||||||
|
Group = "group",
|
||||||
|
User = "user"
|
||||||
|
}
|
||||||
|
|
||||||
export type TCreateAccessApprovalPolicy = {
|
export type TCreateAccessApprovalPolicy = {
|
||||||
approvals: number;
|
approvals: number;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
approvers: ({ type: ApproverType.Group; id: string } | { type: ApproverType.User; id?: string; name?: string })[];
|
approvers: ({ type: ApproverType.Group; id: string } | { type: ApproverType.User; id?: string; username?: string })[];
|
||||||
|
bypassers?: (
|
||||||
|
| { type: BypasserType.Group; id: string }
|
||||||
|
| { type: BypasserType.User; id?: string; username?: string }
|
||||||
|
)[];
|
||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
name: string;
|
name: string;
|
||||||
enforcementLevel: EnforcementLevel;
|
enforcementLevel: EnforcementLevel;
|
||||||
@@ -32,7 +41,11 @@ export type TCreateAccessApprovalPolicy = {
|
|||||||
export type TUpdateAccessApprovalPolicy = {
|
export type TUpdateAccessApprovalPolicy = {
|
||||||
policyId: string;
|
policyId: string;
|
||||||
approvals?: number;
|
approvals?: number;
|
||||||
approvers: ({ type: ApproverType.Group; id: string } | { type: ApproverType.User; id?: string; name?: string })[];
|
approvers: ({ type: ApproverType.Group; id: string } | { type: ApproverType.User; id?: string; username?: string })[];
|
||||||
|
bypassers?: (
|
||||||
|
| { type: BypasserType.Group; id: string }
|
||||||
|
| { type: BypasserType.User; id?: string; username?: string }
|
||||||
|
)[];
|
||||||
secretPath?: string;
|
secretPath?: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
enforcementLevel?: EnforcementLevel;
|
enforcementLevel?: EnforcementLevel;
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import { AccessApprovalRequestsSchema, TableName, TAccessApprovalRequests, TUsers } from "@app/db/schemas";
|
import {
|
||||||
|
AccessApprovalRequestsSchema,
|
||||||
|
TableName,
|
||||||
|
TAccessApprovalRequests,
|
||||||
|
TUserGroupMembership,
|
||||||
|
TUsers
|
||||||
|
} from "@app/db/schemas";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import { ormify, selectAllTableCols, sqlNestRelationships, TFindFilter } from "@app/lib/knex";
|
import { ormify, selectAllTableCols, sqlNestRelationships, TFindFilter } from "@app/lib/knex";
|
||||||
|
|
||||||
@@ -28,12 +34,12 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.AccessApprovalRequest}.policyId`,
|
`${TableName.AccessApprovalRequest}.policyId`,
|
||||||
`${TableName.AccessApprovalPolicy}.id`
|
`${TableName.AccessApprovalPolicy}.id`
|
||||||
)
|
)
|
||||||
|
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
TableName.AccessApprovalRequestReviewer,
|
TableName.AccessApprovalRequestReviewer,
|
||||||
`${TableName.AccessApprovalRequest}.id`,
|
`${TableName.AccessApprovalRequest}.id`,
|
||||||
`${TableName.AccessApprovalRequestReviewer}.requestId`
|
`${TableName.AccessApprovalRequestReviewer}.requestId`
|
||||||
)
|
)
|
||||||
|
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
TableName.AccessApprovalPolicyApprover,
|
TableName.AccessApprovalPolicyApprover,
|
||||||
`${TableName.AccessApprovalPolicy}.id`,
|
`${TableName.AccessApprovalPolicy}.id`,
|
||||||
@@ -46,6 +52,17 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
)
|
)
|
||||||
.leftJoin(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
|
.leftJoin(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
|
||||||
|
|
||||||
|
.leftJoin(
|
||||||
|
TableName.AccessApprovalPolicyBypasser,
|
||||||
|
`${TableName.AccessApprovalPolicy}.id`,
|
||||||
|
`${TableName.AccessApprovalPolicyBypasser}.policyId`
|
||||||
|
)
|
||||||
|
.leftJoin<TUserGroupMembership>(
|
||||||
|
db(TableName.UserGroupMembership).as("bypasserUserGroupMembership"),
|
||||||
|
`${TableName.AccessApprovalPolicyBypasser}.bypasserGroupId`,
|
||||||
|
`bypasserUserGroupMembership.groupId`
|
||||||
|
)
|
||||||
|
|
||||||
.join<TUsers>(
|
.join<TUsers>(
|
||||||
db(TableName.Users).as("requestedByUser"),
|
db(TableName.Users).as("requestedByUser"),
|
||||||
`${TableName.AccessApprovalRequest}.requestedByUserId`,
|
`${TableName.AccessApprovalRequest}.requestedByUserId`,
|
||||||
@@ -69,6 +86,9 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
.select(db.ref("approverUserId").withSchema(TableName.AccessApprovalPolicyApprover))
|
.select(db.ref("approverUserId").withSchema(TableName.AccessApprovalPolicyApprover))
|
||||||
.select(db.ref("userId").withSchema(TableName.UserGroupMembership).as("approverGroupUserId"))
|
.select(db.ref("userId").withSchema(TableName.UserGroupMembership).as("approverGroupUserId"))
|
||||||
|
|
||||||
|
.select(db.ref("bypasserUserId").withSchema(TableName.AccessApprovalPolicyBypasser))
|
||||||
|
.select(db.ref("userId").withSchema("bypasserUserGroupMembership").as("bypasserGroupUserId"))
|
||||||
|
|
||||||
.select(
|
.select(
|
||||||
db.ref("projectId").withSchema(TableName.Environment),
|
db.ref("projectId").withSchema(TableName.Environment),
|
||||||
db.ref("slug").withSchema(TableName.Environment).as("envSlug"),
|
db.ref("slug").withSchema(TableName.Environment).as("envSlug"),
|
||||||
@@ -145,7 +165,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
|
|
||||||
isApproved: !!doc.policyDeletedAt || !!doc.privilegeId
|
isApproved: !!doc.policyDeletedAt || !!doc.privilegeId || doc.status !== ApprovalStatus.PENDING
|
||||||
}),
|
}),
|
||||||
childrenMapper: [
|
childrenMapper: [
|
||||||
{
|
{
|
||||||
@@ -158,6 +178,12 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
key: "approverGroupUserId",
|
key: "approverGroupUserId",
|
||||||
label: "approvers" as const,
|
label: "approvers" as const,
|
||||||
mapper: ({ approverGroupUserId }) => approverGroupUserId
|
mapper: ({ approverGroupUserId }) => approverGroupUserId
|
||||||
|
},
|
||||||
|
{ key: "bypasserUserId", label: "bypassers" as const, mapper: ({ bypasserUserId }) => bypasserUserId },
|
||||||
|
{
|
||||||
|
key: "bypasserGroupUserId",
|
||||||
|
label: "bypassers" as const,
|
||||||
|
mapper: ({ bypasserGroupUserId }) => bypasserGroupUserId
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
@@ -166,7 +192,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
return formattedDocs.map((doc) => ({
|
return formattedDocs.map((doc) => ({
|
||||||
...doc,
|
...doc,
|
||||||
policy: { ...doc.policy, approvers: doc.approvers }
|
policy: { ...doc.policy, approvers: doc.approvers, bypassers: doc.bypassers }
|
||||||
}));
|
}));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "FindRequestsWithPrivilege" });
|
throw new DatabaseError({ error, name: "FindRequestsWithPrivilege" });
|
||||||
@@ -193,7 +219,6 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.AccessApprovalPolicy}.id`,
|
`${TableName.AccessApprovalPolicy}.id`,
|
||||||
`${TableName.AccessApprovalPolicyApprover}.policyId`
|
`${TableName.AccessApprovalPolicyApprover}.policyId`
|
||||||
)
|
)
|
||||||
|
|
||||||
.leftJoin<TUsers>(
|
.leftJoin<TUsers>(
|
||||||
db(TableName.Users).as("accessApprovalPolicyApproverUser"),
|
db(TableName.Users).as("accessApprovalPolicyApproverUser"),
|
||||||
`${TableName.AccessApprovalPolicyApprover}.approverUserId`,
|
`${TableName.AccessApprovalPolicyApprover}.approverUserId`,
|
||||||
@@ -204,13 +229,33 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.AccessApprovalPolicyApprover}.approverGroupId`,
|
`${TableName.AccessApprovalPolicyApprover}.approverGroupId`,
|
||||||
`${TableName.UserGroupMembership}.groupId`
|
`${TableName.UserGroupMembership}.groupId`
|
||||||
)
|
)
|
||||||
|
|
||||||
.leftJoin<TUsers>(
|
.leftJoin<TUsers>(
|
||||||
db(TableName.Users).as("accessApprovalPolicyGroupApproverUser"),
|
db(TableName.Users).as("accessApprovalPolicyGroupApproverUser"),
|
||||||
`${TableName.UserGroupMembership}.userId`,
|
`${TableName.UserGroupMembership}.userId`,
|
||||||
"accessApprovalPolicyGroupApproverUser.id"
|
"accessApprovalPolicyGroupApproverUser.id"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
.leftJoin(
|
||||||
|
TableName.AccessApprovalPolicyBypasser,
|
||||||
|
`${TableName.AccessApprovalPolicy}.id`,
|
||||||
|
`${TableName.AccessApprovalPolicyBypasser}.policyId`
|
||||||
|
)
|
||||||
|
.leftJoin<TUsers>(
|
||||||
|
db(TableName.Users).as("accessApprovalPolicyBypasserUser"),
|
||||||
|
`${TableName.AccessApprovalPolicyBypasser}.bypasserUserId`,
|
||||||
|
"accessApprovalPolicyBypasserUser.id"
|
||||||
|
)
|
||||||
|
.leftJoin<TUserGroupMembership>(
|
||||||
|
db(TableName.UserGroupMembership).as("bypasserUserGroupMembership"),
|
||||||
|
`${TableName.AccessApprovalPolicyBypasser}.bypasserGroupId`,
|
||||||
|
`bypasserUserGroupMembership.groupId`
|
||||||
|
)
|
||||||
|
.leftJoin<TUsers>(
|
||||||
|
db(TableName.Users).as("accessApprovalPolicyGroupBypasserUser"),
|
||||||
|
`bypasserUserGroupMembership.userId`,
|
||||||
|
"accessApprovalPolicyGroupBypasserUser.id"
|
||||||
|
)
|
||||||
|
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
TableName.AccessApprovalRequestReviewer,
|
TableName.AccessApprovalRequestReviewer,
|
||||||
`${TableName.AccessApprovalRequest}.id`,
|
`${TableName.AccessApprovalRequest}.id`,
|
||||||
@@ -241,6 +286,18 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
tx.ref("firstName").withSchema("requestedByUser").as("requestedByUserFirstName"),
|
tx.ref("firstName").withSchema("requestedByUser").as("requestedByUserFirstName"),
|
||||||
tx.ref("lastName").withSchema("requestedByUser").as("requestedByUserLastName"),
|
tx.ref("lastName").withSchema("requestedByUser").as("requestedByUserLastName"),
|
||||||
|
|
||||||
|
// Bypassers
|
||||||
|
tx.ref("bypasserUserId").withSchema(TableName.AccessApprovalPolicyBypasser),
|
||||||
|
tx.ref("userId").withSchema("bypasserUserGroupMembership").as("bypasserGroupUserId"),
|
||||||
|
tx.ref("email").withSchema("accessApprovalPolicyBypasserUser").as("bypasserEmail"),
|
||||||
|
tx.ref("email").withSchema("accessApprovalPolicyGroupBypasserUser").as("bypasserGroupEmail"),
|
||||||
|
tx.ref("username").withSchema("accessApprovalPolicyBypasserUser").as("bypasserUsername"),
|
||||||
|
tx.ref("username").withSchema("accessApprovalPolicyGroupBypasserUser").as("bypasserGroupUsername"),
|
||||||
|
tx.ref("firstName").withSchema("accessApprovalPolicyBypasserUser").as("bypasserFirstName"),
|
||||||
|
tx.ref("firstName").withSchema("accessApprovalPolicyGroupBypasserUser").as("bypasserGroupFirstName"),
|
||||||
|
tx.ref("lastName").withSchema("accessApprovalPolicyBypasserUser").as("bypasserLastName"),
|
||||||
|
tx.ref("lastName").withSchema("accessApprovalPolicyGroupBypasserUser").as("bypasserGroupLastName"),
|
||||||
|
|
||||||
tx.ref("reviewerUserId").withSchema(TableName.AccessApprovalRequestReviewer),
|
tx.ref("reviewerUserId").withSchema(TableName.AccessApprovalRequestReviewer),
|
||||||
|
|
||||||
tx.ref("status").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerStatus"),
|
tx.ref("status").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerStatus"),
|
||||||
@@ -265,7 +322,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
try {
|
try {
|
||||||
const sql = findQuery({ [`${TableName.AccessApprovalRequest}.id` as "id"]: id }, tx || db.replicaNode());
|
const sql = findQuery({ [`${TableName.AccessApprovalRequest}.id` as "id"]: id }, tx || db.replicaNode());
|
||||||
const docs = await sql;
|
const docs = await sql;
|
||||||
const formatedDoc = sqlNestRelationships({
|
const formattedDoc = sqlNestRelationships({
|
||||||
data: docs,
|
data: docs,
|
||||||
key: "id",
|
key: "id",
|
||||||
parentMapper: (el) => ({
|
parentMapper: (el) => ({
|
||||||
@@ -335,13 +392,51 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
lastName,
|
lastName,
|
||||||
username
|
username
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "bypasserUserId",
|
||||||
|
label: "bypassers" as const,
|
||||||
|
mapper: ({
|
||||||
|
bypasserUserId,
|
||||||
|
bypasserEmail: email,
|
||||||
|
bypasserUsername: username,
|
||||||
|
bypasserLastName: lastName,
|
||||||
|
bypasserFirstName: firstName
|
||||||
|
}) => ({
|
||||||
|
userId: bypasserUserId,
|
||||||
|
email,
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
username
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "bypasserGroupUserId",
|
||||||
|
label: "bypassers" as const,
|
||||||
|
mapper: ({
|
||||||
|
userId,
|
||||||
|
bypasserGroupEmail: email,
|
||||||
|
bypasserGroupUsername: username,
|
||||||
|
bypasserGroupLastName: lastName,
|
||||||
|
bypasserFirstName: firstName
|
||||||
|
}) => ({
|
||||||
|
userId,
|
||||||
|
email,
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
username
|
||||||
|
})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
if (!formatedDoc?.[0]) return;
|
if (!formattedDoc?.[0]) return;
|
||||||
return {
|
return {
|
||||||
...formatedDoc[0],
|
...formattedDoc[0],
|
||||||
policy: { ...formatedDoc[0].policy, approvers: formatedDoc[0].approvers }
|
policy: {
|
||||||
|
...formattedDoc[0].policy,
|
||||||
|
approvers: formattedDoc[0].approvers,
|
||||||
|
bypassers: formattedDoc[0].bypassers
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "FindByIdAccessApprovalRequest" });
|
throw new DatabaseError({ error, name: "FindByIdAccessApprovalRequest" });
|
||||||
@@ -392,14 +487,20 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
// an approval is pending if there is no reviewer rejections and no privilege ID is set
|
// an approval is pending if there is no reviewer rejections, no privilege ID is set and the status is pending
|
||||||
const pendingApprovals = formattedRequests.filter(
|
const pendingApprovals = formattedRequests.filter(
|
||||||
(req) => !req.privilegeId && !req.reviewers.some((r) => r.status === ApprovalStatus.REJECTED)
|
(req) =>
|
||||||
|
!req.privilegeId &&
|
||||||
|
!req.reviewers.some((r) => r.status === ApprovalStatus.REJECTED) &&
|
||||||
|
req.status === ApprovalStatus.PENDING
|
||||||
);
|
);
|
||||||
|
|
||||||
// an approval is finalized if there are any rejections or a privilege ID is set
|
// an approval is finalized if there are any rejections, a privilege ID is set or the number of approvals is equal to the number of approvals required
|
||||||
const finalizedApprovals = formattedRequests.filter(
|
const finalizedApprovals = formattedRequests.filter(
|
||||||
(req) => req.privilegeId || req.reviewers.some((r) => r.status === ApprovalStatus.REJECTED)
|
(req) =>
|
||||||
|
req.privilegeId ||
|
||||||
|
req.reviewers.some((r) => r.status === ApprovalStatus.REJECTED) ||
|
||||||
|
req.status !== ApprovalStatus.PENDING
|
||||||
);
|
);
|
||||||
|
|
||||||
return { pendingCount: pendingApprovals.length, finalizedCount: finalizedApprovals.length };
|
return { pendingCount: pendingApprovals.length, finalizedCount: finalizedApprovals.length };
|
||||||
|
|||||||
+113
-29
@@ -6,6 +6,7 @@ import { getConfig } from "@app/lib/config/env";
|
|||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { ms } from "@app/lib/ms";
|
import { ms } from "@app/lib/ms";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
|
import { EnforcementLevel } from "@app/lib/types";
|
||||||
import { triggerWorkflowIntegrationNotification } from "@app/lib/workflow-integrations/trigger-notification";
|
import { triggerWorkflowIntegrationNotification } from "@app/lib/workflow-integrations/trigger-notification";
|
||||||
import { TriggerFeature } from "@app/lib/workflow-integrations/types";
|
import { TriggerFeature } from "@app/lib/workflow-integrations/types";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
@@ -55,7 +56,7 @@ type TSecretApprovalRequestServiceFactoryDep = {
|
|||||||
| "findOne"
|
| "findOne"
|
||||||
| "getCount"
|
| "getCount"
|
||||||
>;
|
>;
|
||||||
accessApprovalPolicyDAL: Pick<TAccessApprovalPolicyDALFactory, "findOne" | "find">;
|
accessApprovalPolicyDAL: Pick<TAccessApprovalPolicyDALFactory, "findOne" | "find" | "findLastValidPolicy">;
|
||||||
accessApprovalRequestReviewerDAL: Pick<
|
accessApprovalRequestReviewerDAL: Pick<
|
||||||
TAccessApprovalRequestReviewerDALFactory,
|
TAccessApprovalRequestReviewerDALFactory,
|
||||||
"create" | "find" | "findOne" | "transaction"
|
"create" | "find" | "findOne" | "transaction"
|
||||||
@@ -130,7 +131,7 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
|
|
||||||
if (!environment) throw new NotFoundError({ message: `Environment with slug '${envSlug}' not found` });
|
if (!environment) throw new NotFoundError({ message: `Environment with slug '${envSlug}' not found` });
|
||||||
|
|
||||||
const policy = await accessApprovalPolicyDAL.findOne({
|
const policy = await accessApprovalPolicyDAL.findLastValidPolicy({
|
||||||
envId: environment.id,
|
envId: environment.id,
|
||||||
secretPath
|
secretPath
|
||||||
});
|
});
|
||||||
@@ -202,7 +203,7 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
|
|
||||||
const isRejected = reviewers.some((reviewer) => reviewer.status === ApprovalStatus.REJECTED);
|
const isRejected = reviewers.some((reviewer) => reviewer.status === ApprovalStatus.REJECTED);
|
||||||
|
|
||||||
if (!isRejected) {
|
if (!isRejected && duplicateRequest.status === ApprovalStatus.PENDING) {
|
||||||
throw new BadRequestError({ message: "You already have a pending access request with the same criteria" });
|
throw new BadRequestError({ message: "You already have a pending access request with the same criteria" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -323,24 +324,20 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
status,
|
status,
|
||||||
actorId,
|
actorId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
bypassReason
|
||||||
}: TReviewAccessRequestDTO) => {
|
}: TReviewAccessRequestDTO) => {
|
||||||
const accessApprovalRequest = await accessApprovalRequestDAL.findById(requestId);
|
const accessApprovalRequest = await accessApprovalRequestDAL.findById(requestId);
|
||||||
if (!accessApprovalRequest) {
|
if (!accessApprovalRequest) {
|
||||||
throw new NotFoundError({ message: `Secret approval request with ID '${requestId}' not found` });
|
throw new NotFoundError({ message: `Secret approval request with ID '${requestId}' not found` });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { policy } = accessApprovalRequest;
|
const { policy, environment } = accessApprovalRequest;
|
||||||
if (policy.deletedAt) {
|
if (policy.deletedAt) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: "The policy associated with this access request has been deleted."
|
message: "The policy associated with this access request has been deleted."
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (!policy.allowedSelfApprovals && actorId === accessApprovalRequest.requestedByUserId) {
|
|
||||||
throw new BadRequestError({
|
|
||||||
message: "Failed to review access approval request. Users are not authorized to review their own request."
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const { membership, hasRole } = await permissionService.getProjectPermission({
|
const { membership, hasRole } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
@@ -355,29 +352,71 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
throw new ForbiddenRequestError({ message: "You are not a member of this project" });
|
throw new ForbiddenRequestError({ message: "You are not a member of this project" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isSelfApproval = actorId === accessApprovalRequest.requestedByUserId;
|
||||||
|
const isSoftEnforcement = policy.enforcementLevel === EnforcementLevel.Soft;
|
||||||
|
const canBypass = !policy.bypassers.length || policy.bypassers.some((bypasser) => bypasser.userId === actorId);
|
||||||
|
const cannotBypassUnderSoftEnforcement = !(isSoftEnforcement && canBypass);
|
||||||
|
|
||||||
|
const isApprover = policy.approvers.find((approver) => approver.userId === actorId);
|
||||||
|
|
||||||
|
// If user is (not an approver OR cant self approve) AND can't bypass policy
|
||||||
|
if ((!isApprover || (!policy.allowedSelfApprovals && isSelfApproval)) && cannotBypassUnderSoftEnforcement) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Failed to review access approval request. Users are not authorized to review their own request."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!hasRole(ProjectMembershipRole.Admin) &&
|
!hasRole(ProjectMembershipRole.Admin) &&
|
||||||
accessApprovalRequest.requestedByUserId !== actorId && // The request wasn't made by the current user
|
accessApprovalRequest.requestedByUserId !== actorId && // The request wasn't made by the current user
|
||||||
!policy.approvers.find((approver) => approver.userId === actorId) // The request isn't performed by an assigned approver
|
!isApprover // The request isn't performed by an assigned approver
|
||||||
) {
|
) {
|
||||||
throw new ForbiddenRequestError({ message: "You are not authorized to approve this request" });
|
throw new ForbiddenRequestError({ message: "You are not authorized to approve this request" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const project = await projectDAL.findById(accessApprovalRequest.projectId);
|
||||||
|
if (!project) {
|
||||||
|
throw new NotFoundError({ message: "The project associated with this access request was not found." });
|
||||||
|
}
|
||||||
|
|
||||||
const existingReviews = await accessApprovalRequestReviewerDAL.find({ requestId: accessApprovalRequest.id });
|
const existingReviews = await accessApprovalRequestReviewerDAL.find({ requestId: accessApprovalRequest.id });
|
||||||
if (existingReviews.some((review) => review.status === ApprovalStatus.REJECTED)) {
|
if (existingReviews.some((review) => review.status === ApprovalStatus.REJECTED)) {
|
||||||
throw new BadRequestError({ message: "The request has already been rejected by another reviewer" });
|
throw new BadRequestError({ message: "The request has already been rejected by another reviewer" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const reviewStatus = await accessApprovalRequestReviewerDAL.transaction(async (tx) => {
|
const reviewStatus = await accessApprovalRequestReviewerDAL.transaction(async (tx) => {
|
||||||
const review = await accessApprovalRequestReviewerDAL.findOne(
|
const isBreakGlassApprovalAttempt =
|
||||||
|
policy.enforcementLevel === EnforcementLevel.Soft &&
|
||||||
|
actorId === accessApprovalRequest.requestedByUserId &&
|
||||||
|
status === ApprovalStatus.APPROVED;
|
||||||
|
|
||||||
|
let reviewForThisActorProcessing: {
|
||||||
|
id: string;
|
||||||
|
requestId: string;
|
||||||
|
reviewerUserId: string;
|
||||||
|
status: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
};
|
||||||
|
|
||||||
|
const existingReviewByActorInTx = await accessApprovalRequestReviewerDAL.findOne(
|
||||||
{
|
{
|
||||||
requestId: accessApprovalRequest.id,
|
requestId: accessApprovalRequest.id,
|
||||||
reviewerUserId: actorId
|
reviewerUserId: actorId
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
if (!review) {
|
|
||||||
const newReview = await accessApprovalRequestReviewerDAL.create(
|
// Check if review exists for actor
|
||||||
|
if (existingReviewByActorInTx) {
|
||||||
|
// Check if breakglass re-approval
|
||||||
|
if (isBreakGlassApprovalAttempt && existingReviewByActorInTx.status === ApprovalStatus.APPROVED) {
|
||||||
|
reviewForThisActorProcessing = existingReviewByActorInTx;
|
||||||
|
} else {
|
||||||
|
throw new BadRequestError({ message: "You have already reviewed this request" });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reviewForThisActorProcessing = await accessApprovalRequestReviewerDAL.create(
|
||||||
{
|
{
|
||||||
status,
|
status,
|
||||||
requestId: accessApprovalRequest.id,
|
requestId: accessApprovalRequest.id,
|
||||||
@@ -385,19 +424,26 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const allReviews = [...existingReviews, newReview];
|
const otherReviews = existingReviews.filter((er) => er.reviewerUserId !== actorId);
|
||||||
|
const allUniqueReviews = [...otherReviews, reviewForThisActorProcessing];
|
||||||
|
|
||||||
const approvedReviews = allReviews.filter((r) => r.status === ApprovalStatus.APPROVED);
|
const approvedReviews = allUniqueReviews.filter((r) => r.status === ApprovalStatus.APPROVED);
|
||||||
|
const meetsStandardApprovalThreshold = approvedReviews.length >= policy.approvals;
|
||||||
|
|
||||||
// approvals is the required number of approvals. If the number of approved reviews is equal to the number of required approvals, then the request is approved.
|
if (
|
||||||
if (approvedReviews.length === policy.approvals) {
|
reviewForThisActorProcessing.status === ApprovalStatus.APPROVED &&
|
||||||
|
(meetsStandardApprovalThreshold || isBreakGlassApprovalAttempt)
|
||||||
|
) {
|
||||||
|
const currentRequestState = await accessApprovalRequestDAL.findById(accessApprovalRequest.id, tx);
|
||||||
|
let privilegeIdToSet = currentRequestState?.privilegeId || null;
|
||||||
|
|
||||||
|
if (!privilegeIdToSet) {
|
||||||
if (accessApprovalRequest.isTemporary && !accessApprovalRequest.temporaryRange) {
|
if (accessApprovalRequest.isTemporary && !accessApprovalRequest.temporaryRange) {
|
||||||
throw new BadRequestError({ message: "Temporary range is required for temporary access" });
|
throw new BadRequestError({ message: "Temporary range is required for temporary access" });
|
||||||
}
|
}
|
||||||
|
|
||||||
let privilegeId: string | null = null;
|
|
||||||
|
|
||||||
if (!accessApprovalRequest.isTemporary && !accessApprovalRequest.temporaryRange) {
|
if (!accessApprovalRequest.isTemporary && !accessApprovalRequest.temporaryRange) {
|
||||||
// Permanent access
|
// Permanent access
|
||||||
const privilege = await additionalPrivilegeDAL.create(
|
const privilege = await additionalPrivilegeDAL.create(
|
||||||
@@ -409,7 +455,7 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
privilegeId = privilege.id;
|
privilegeIdToSet = privilege.id;
|
||||||
} else {
|
} else {
|
||||||
// Temporary access
|
// Temporary access
|
||||||
const relativeTempAllocatedTimeInMs = ms(accessApprovalRequest.temporaryRange!);
|
const relativeTempAllocatedTimeInMs = ms(accessApprovalRequest.temporaryRange!);
|
||||||
@@ -421,23 +467,61 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
projectId: accessApprovalRequest.projectId,
|
projectId: accessApprovalRequest.projectId,
|
||||||
slug: `requested-privilege-${slugify(alphaNumericNanoId(12))}`,
|
slug: `requested-privilege-${slugify(alphaNumericNanoId(12))}`,
|
||||||
permissions: JSON.stringify(accessApprovalRequest.permissions),
|
permissions: JSON.stringify(accessApprovalRequest.permissions),
|
||||||
isTemporary: true,
|
isTemporary: true, // Explicitly set to true for the privilege
|
||||||
temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative,
|
temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative,
|
||||||
temporaryRange: accessApprovalRequest.temporaryRange!,
|
temporaryRange: accessApprovalRequest.temporaryRange!,
|
||||||
temporaryAccessStartTime: startTime,
|
temporaryAccessStartTime: startTime,
|
||||||
temporaryAccessEndTime: new Date(new Date(startTime).getTime() + relativeTempAllocatedTimeInMs)
|
temporaryAccessEndTime: new Date(startTime.getTime() + relativeTempAllocatedTimeInMs)
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
privilegeId = privilege.id;
|
privilegeIdToSet = privilege.id;
|
||||||
}
|
}
|
||||||
|
await accessApprovalRequestDAL.updateById(
|
||||||
await accessApprovalRequestDAL.updateById(accessApprovalRequest.id, { privilegeId }, tx);
|
accessApprovalRequest.id,
|
||||||
|
{ privilegeId: privilegeIdToSet, status: ApprovalStatus.APPROVED },
|
||||||
|
tx
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return newReview;
|
|
||||||
}
|
}
|
||||||
throw new BadRequestError({ message: "You have already reviewed this request" });
|
|
||||||
|
// Send notification if this was a breakglass approval
|
||||||
|
if (isBreakGlassApprovalAttempt) {
|
||||||
|
const cfg = getConfig();
|
||||||
|
const actingUser = await userDAL.findById(actorId, tx);
|
||||||
|
|
||||||
|
if (actingUser) {
|
||||||
|
const policyApproverUserIds = policy.approvers
|
||||||
|
.map((ap) => ap.userId)
|
||||||
|
.filter((id): id is string => typeof id === "string");
|
||||||
|
|
||||||
|
if (policyApproverUserIds.length > 0) {
|
||||||
|
const approverUsersForEmail = await userDAL.find({ $in: { id: policyApproverUserIds } }, { tx });
|
||||||
|
const recipientEmails = approverUsersForEmail
|
||||||
|
.map((appUser) => appUser.email)
|
||||||
|
.filter((email): email is string => !!email);
|
||||||
|
|
||||||
|
if (recipientEmails.length > 0) {
|
||||||
|
await smtpService.sendMail({
|
||||||
|
recipients: recipientEmails,
|
||||||
|
subjectLine: "Infisical Secret Access Policy Bypassed",
|
||||||
|
substitutions: {
|
||||||
|
projectName: project.name,
|
||||||
|
requesterFullName: `${actingUser.firstName} ${actingUser.lastName}`,
|
||||||
|
requesterEmail: actingUser.email,
|
||||||
|
bypassReason: bypassReason || "No reason provided",
|
||||||
|
secretPath: policy.secretPath || "/",
|
||||||
|
environment,
|
||||||
|
approvalUrl: `${cfg.SITE_URL}/secret-manager/${project.id}/approval`,
|
||||||
|
requestType: "access"
|
||||||
|
},
|
||||||
|
template: SmtpTemplates.AccessSecretRequestBypassed
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reviewForThisActorProcessing;
|
||||||
});
|
});
|
||||||
|
|
||||||
return reviewStatus;
|
return reviewStatus;
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ export type TGetAccessRequestCountDTO = {
|
|||||||
export type TReviewAccessRequestDTO = {
|
export type TReviewAccessRequestDTO = {
|
||||||
requestId: string;
|
requestId: string;
|
||||||
status: ApprovalStatus;
|
status: ApprovalStatus;
|
||||||
|
envName?: string;
|
||||||
|
bypassReason?: string;
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
export type TCreateAccessApprovalRequestDTO = {
|
export type TCreateAccessApprovalRequestDTO = {
|
||||||
|
|||||||
+23
-2
@@ -1,7 +1,9 @@
|
|||||||
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { OrgServiceActor } from "@app/lib/types";
|
import { OrgServiceActor } from "@app/lib/types";
|
||||||
|
|
||||||
import { AppConnection } from "../app-connection-enums";
|
import { AppConnection } from "../../../../services/app-connection/app-connection-enums";
|
||||||
|
import { TLicenseServiceFactory } from "../../license/license-service";
|
||||||
import { listOCICompartments, listOCIVaultKeys, listOCIVaults } from "./oci-connection-fns";
|
import { listOCICompartments, listOCIVaultKeys, listOCIVaults } from "./oci-connection-fns";
|
||||||
import { TOCIConnection } from "./oci-connection-types";
|
import { TOCIConnection } from "./oci-connection-types";
|
||||||
|
|
||||||
@@ -22,8 +24,23 @@ type TListOCIVaultKeysDTO = {
|
|||||||
vaultOcid: string;
|
vaultOcid: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ociConnectionService = (getAppConnection: TGetAppConnectionFunc) => {
|
// Enterprise check
|
||||||
|
export const checkPlan = async (licenseService: Pick<TLicenseServiceFactory, "getPlan">, orgId: string) => {
|
||||||
|
const plan = await licenseService.getPlan(orgId);
|
||||||
|
if (!plan.enterpriseAppConnections)
|
||||||
|
throw new BadRequestError({
|
||||||
|
message:
|
||||||
|
"Failed to use app connection due to plan restriction. Upgrade plan to access enterprise app connections."
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ociConnectionService = (
|
||||||
|
getAppConnection: TGetAppConnectionFunc,
|
||||||
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">
|
||||||
|
) => {
|
||||||
const listCompartments = async (connectionId: string, actor: OrgServiceActor) => {
|
const listCompartments = async (connectionId: string, actor: OrgServiceActor) => {
|
||||||
|
await checkPlan(licenseService, actor.orgId);
|
||||||
|
|
||||||
const appConnection = await getAppConnection(AppConnection.OCI, connectionId, actor);
|
const appConnection = await getAppConnection(AppConnection.OCI, connectionId, actor);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -36,6 +53,8 @@ export const ociConnectionService = (getAppConnection: TGetAppConnectionFunc) =>
|
|||||||
};
|
};
|
||||||
|
|
||||||
const listVaults = async ({ connectionId, compartmentOcid }: TListOCIVaultsDTO, actor: OrgServiceActor) => {
|
const listVaults = async ({ connectionId, compartmentOcid }: TListOCIVaultsDTO, actor: OrgServiceActor) => {
|
||||||
|
await checkPlan(licenseService, actor.orgId);
|
||||||
|
|
||||||
const appConnection = await getAppConnection(AppConnection.OCI, connectionId, actor);
|
const appConnection = await getAppConnection(AppConnection.OCI, connectionId, actor);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -51,6 +70,8 @@ export const ociConnectionService = (getAppConnection: TGetAppConnectionFunc) =>
|
|||||||
{ connectionId, compartmentOcid, vaultOcid }: TListOCIVaultKeysDTO,
|
{ connectionId, compartmentOcid, vaultOcid }: TListOCIVaultKeysDTO,
|
||||||
actor: OrgServiceActor
|
actor: OrgServiceActor
|
||||||
) => {
|
) => {
|
||||||
|
await checkPlan(licenseService, actor.orgId);
|
||||||
|
|
||||||
const appConnection = await getAppConnection(AppConnection.OCI, connectionId, actor);
|
const appConnection = await getAppConnection(AppConnection.OCI, connectionId, actor);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
+1
-1
@@ -2,7 +2,7 @@ import z from "zod";
|
|||||||
|
|
||||||
import { DiscriminativePick } from "@app/lib/types";
|
import { DiscriminativePick } from "@app/lib/types";
|
||||||
|
|
||||||
import { AppConnection } from "../app-connection-enums";
|
import { AppConnection } from "../../../../services/app-connection/app-connection-enums";
|
||||||
import {
|
import {
|
||||||
CreateOCIConnectionSchema,
|
CreateOCIConnectionSchema,
|
||||||
OCIConnectionSchema,
|
OCIConnectionSchema,
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { ProjectType } from "@app/db/schemas";
|
||||||
import {
|
import {
|
||||||
TCreateProjectTemplateDTO,
|
TCreateProjectTemplateDTO,
|
||||||
TUpdateProjectTemplateDTO
|
TUpdateProjectTemplateDTO
|
||||||
@@ -32,7 +33,7 @@ import { AppConnection } from "@app/services/app-connection/app-connection-enums
|
|||||||
import { TCreateAppConnectionDTO, TUpdateAppConnectionDTO } from "@app/services/app-connection/app-connection-types";
|
import { TCreateAppConnectionDTO, TUpdateAppConnectionDTO } from "@app/services/app-connection/app-connection-types";
|
||||||
import { ActorType } from "@app/services/auth/auth-type";
|
import { ActorType } from "@app/services/auth/auth-type";
|
||||||
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "@app/services/certificate/certificate-types";
|
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "@app/services/certificate/certificate-types";
|
||||||
import { CaStatus } from "@app/services/certificate-authority/certificate-authority-types";
|
import { CaStatus } from "@app/services/certificate-authority/certificate-authority-enums";
|
||||||
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
|
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
|
||||||
import { TAllowedFields } from "@app/services/identity-ldap-auth/identity-ldap-auth-types";
|
import { TAllowedFields } from "@app/services/identity-ldap-auth/identity-ldap-auth-types";
|
||||||
import { PkiItemType } from "@app/services/pki-collection/pki-collection-types";
|
import { PkiItemType } from "@app/services/pki-collection/pki-collection-types";
|
||||||
@@ -243,6 +244,7 @@ export enum EventType {
|
|||||||
REMOVE_HOST_FROM_SSH_HOST_GROUP = "remove-host-from-ssh-host-group",
|
REMOVE_HOST_FROM_SSH_HOST_GROUP = "remove-host-from-ssh-host-group",
|
||||||
CREATE_CA = "create-certificate-authority",
|
CREATE_CA = "create-certificate-authority",
|
||||||
GET_CA = "get-certificate-authority",
|
GET_CA = "get-certificate-authority",
|
||||||
|
GET_CAS = "get-certificate-authorities",
|
||||||
UPDATE_CA = "update-certificate-authority",
|
UPDATE_CA = "update-certificate-authority",
|
||||||
DELETE_CA = "delete-certificate-authority",
|
DELETE_CA = "delete-certificate-authority",
|
||||||
RENEW_CA = "renew-certificate-authority",
|
RENEW_CA = "renew-certificate-authority",
|
||||||
@@ -253,6 +255,7 @@ export enum EventType {
|
|||||||
IMPORT_CA_CERT = "import-certificate-authority-cert",
|
IMPORT_CA_CERT = "import-certificate-authority-cert",
|
||||||
GET_CA_CRLS = "get-certificate-authority-crls",
|
GET_CA_CRLS = "get-certificate-authority-crls",
|
||||||
ISSUE_CERT = "issue-cert",
|
ISSUE_CERT = "issue-cert",
|
||||||
|
IMPORT_CERT = "import-cert",
|
||||||
SIGN_CERT = "sign-cert",
|
SIGN_CERT = "sign-cert",
|
||||||
GET_CA_CERTIFICATE_TEMPLATES = "get-ca-certificate-templates",
|
GET_CA_CERTIFICATE_TEMPLATES = "get-ca-certificate-templates",
|
||||||
GET_CERT = "get-cert",
|
GET_CERT = "get-cert",
|
||||||
@@ -278,7 +281,9 @@ export enum EventType {
|
|||||||
GET_PKI_SUBSCRIBER = "get-pki-subscriber",
|
GET_PKI_SUBSCRIBER = "get-pki-subscriber",
|
||||||
ISSUE_PKI_SUBSCRIBER_CERT = "issue-pki-subscriber-cert",
|
ISSUE_PKI_SUBSCRIBER_CERT = "issue-pki-subscriber-cert",
|
||||||
SIGN_PKI_SUBSCRIBER_CERT = "sign-pki-subscriber-cert",
|
SIGN_PKI_SUBSCRIBER_CERT = "sign-pki-subscriber-cert",
|
||||||
|
AUTOMATED_RENEW_SUBSCRIBER_CERT = "automated-renew-subscriber-cert",
|
||||||
LIST_PKI_SUBSCRIBER_CERTS = "list-pki-subscriber-certs",
|
LIST_PKI_SUBSCRIBER_CERTS = "list-pki-subscriber-certs",
|
||||||
|
GET_SUBSCRIBER_ACTIVE_CERT_BUNDLE = "get-subscriber-active-cert-bundle",
|
||||||
CREATE_KMS = "create-kms",
|
CREATE_KMS = "create-kms",
|
||||||
UPDATE_KMS = "update-kms",
|
UPDATE_KMS = "update-kms",
|
||||||
DELETE_KMS = "delete-kms",
|
DELETE_KMS = "delete-kms",
|
||||||
@@ -327,7 +332,6 @@ export enum EventType {
|
|||||||
CREATE_PROJECT_TEMPLATE = "create-project-template",
|
CREATE_PROJECT_TEMPLATE = "create-project-template",
|
||||||
UPDATE_PROJECT_TEMPLATE = "update-project-template",
|
UPDATE_PROJECT_TEMPLATE = "update-project-template",
|
||||||
DELETE_PROJECT_TEMPLATE = "delete-project-template",
|
DELETE_PROJECT_TEMPLATE = "delete-project-template",
|
||||||
APPLY_PROJECT_TEMPLATE = "apply-project-template",
|
|
||||||
GET_APP_CONNECTIONS = "get-app-connections",
|
GET_APP_CONNECTIONS = "get-app-connections",
|
||||||
GET_AVAILABLE_APP_CONNECTIONS_DETAILS = "get-available-app-connections-details",
|
GET_AVAILABLE_APP_CONNECTIONS_DETAILS = "get-available-app-connections-details",
|
||||||
GET_APP_CONNECTION = "get-app-connection",
|
GET_APP_CONNECTION = "get-app-connection",
|
||||||
@@ -401,7 +405,13 @@ export enum EventType {
|
|||||||
SECRET_SCANNING_FINDING_LIST = "secret-scanning-finding-list",
|
SECRET_SCANNING_FINDING_LIST = "secret-scanning-finding-list",
|
||||||
SECRET_SCANNING_FINDING_UPDATE = "secret-scanning-finding-update",
|
SECRET_SCANNING_FINDING_UPDATE = "secret-scanning-finding-update",
|
||||||
SECRET_SCANNING_CONFIG_GET = "secret-scanning-config-get",
|
SECRET_SCANNING_CONFIG_GET = "secret-scanning-config-get",
|
||||||
SECRET_SCANNING_CONFIG_UPDATE = "secret-scanning-config-update"
|
SECRET_SCANNING_CONFIG_UPDATE = "secret-scanning-config-update",
|
||||||
|
|
||||||
|
UPDATE_ORG = "update-org",
|
||||||
|
|
||||||
|
CREATE_PROJECT = "create-project",
|
||||||
|
UPDATE_PROJECT = "update-project",
|
||||||
|
DELETE_PROJECT = "delete-project"
|
||||||
}
|
}
|
||||||
|
|
||||||
export const filterableSecretEvents: EventType[] = [
|
export const filterableSecretEvents: EventType[] = [
|
||||||
@@ -1798,7 +1808,8 @@ interface CreateCa {
|
|||||||
type: EventType.CREATE_CA;
|
type: EventType.CREATE_CA;
|
||||||
metadata: {
|
metadata: {
|
||||||
caId: string;
|
caId: string;
|
||||||
dn: string;
|
name: string;
|
||||||
|
dn?: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1806,7 +1817,15 @@ interface GetCa {
|
|||||||
type: EventType.GET_CA;
|
type: EventType.GET_CA;
|
||||||
metadata: {
|
metadata: {
|
||||||
caId: string;
|
caId: string;
|
||||||
dn: string;
|
name: string;
|
||||||
|
dn?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GetCAs {
|
||||||
|
type: EventType.GET_CAS;
|
||||||
|
metadata: {
|
||||||
|
caIds: string[];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1814,7 +1833,8 @@ interface UpdateCa {
|
|||||||
type: EventType.UPDATE_CA;
|
type: EventType.UPDATE_CA;
|
||||||
metadata: {
|
metadata: {
|
||||||
caId: string;
|
caId: string;
|
||||||
dn: string;
|
name: string;
|
||||||
|
dn?: string;
|
||||||
status: CaStatus;
|
status: CaStatus;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1823,7 +1843,8 @@ interface DeleteCa {
|
|||||||
type: EventType.DELETE_CA;
|
type: EventType.DELETE_CA;
|
||||||
metadata: {
|
metadata: {
|
||||||
caId: string;
|
caId: string;
|
||||||
dn: string;
|
name: string;
|
||||||
|
dn?: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1893,6 +1914,15 @@ interface IssueCert {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ImportCert {
|
||||||
|
type: EventType.IMPORT_CERT;
|
||||||
|
metadata: {
|
||||||
|
certId: string;
|
||||||
|
cn: string;
|
||||||
|
serialNumber: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface SignCert {
|
interface SignCert {
|
||||||
type: EventType.SIGN_CERT;
|
type: EventType.SIGN_CERT;
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -2060,7 +2090,7 @@ interface CreatePkiSubscriber {
|
|||||||
caId?: string;
|
caId?: string;
|
||||||
name: string;
|
name: string;
|
||||||
commonName: string;
|
commonName: string;
|
||||||
ttl: string;
|
ttl?: string;
|
||||||
subjectAlternativeNames: string[];
|
subjectAlternativeNames: string[];
|
||||||
keyUsages: CertKeyUsage[];
|
keyUsages: CertKeyUsage[];
|
||||||
extendedKeyUsages: CertExtendedKeyUsage[];
|
extendedKeyUsages: CertExtendedKeyUsage[];
|
||||||
@@ -2102,7 +2132,15 @@ interface IssuePkiSubscriberCert {
|
|||||||
metadata: {
|
metadata: {
|
||||||
subscriberId: string;
|
subscriberId: string;
|
||||||
name: string;
|
name: string;
|
||||||
serialNumber: string;
|
serialNumber?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AutomatedRenewPkiSubscriberCert {
|
||||||
|
type: EventType.AUTOMATED_RENEW_SUBSCRIBER_CERT;
|
||||||
|
metadata: {
|
||||||
|
subscriberId: string;
|
||||||
|
name: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2124,6 +2162,16 @@ interface ListPkiSubscriberCerts {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface GetSubscriberActiveCertBundle {
|
||||||
|
type: EventType.GET_SUBSCRIBER_ACTIVE_CERT_BUNDLE;
|
||||||
|
metadata: {
|
||||||
|
subscriberId: string;
|
||||||
|
name: string;
|
||||||
|
certId: string;
|
||||||
|
serialNumber: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface CreateKmsEvent {
|
interface CreateKmsEvent {
|
||||||
type: EventType.CREATE_KMS;
|
type: EventType.CREATE_KMS;
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -2477,14 +2525,6 @@ interface DeleteProjectTemplateEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ApplyProjectTemplateEvent {
|
|
||||||
type: EventType.APPLY_PROJECT_TEMPLATE;
|
|
||||||
metadata: {
|
|
||||||
template: string;
|
|
||||||
projectId: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GetAppConnectionsEvent {
|
interface GetAppConnectionsEvent {
|
||||||
type: EventType.GET_APP_CONNECTIONS;
|
type: EventType.GET_APP_CONNECTIONS;
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -3034,6 +3074,59 @@ interface SecretScanningConfigReadEvent {
|
|||||||
metadata?: Record<string, never>; // not needed, based off projectId
|
metadata?: Record<string, never>; // not needed, based off projectId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface OrgUpdateEvent {
|
||||||
|
type: EventType.UPDATE_ORG;
|
||||||
|
metadata: {
|
||||||
|
name?: string;
|
||||||
|
slug?: string;
|
||||||
|
authEnforced?: boolean;
|
||||||
|
scimEnabled?: boolean;
|
||||||
|
defaultMembershipRoleSlug?: string;
|
||||||
|
enforceMfa?: boolean;
|
||||||
|
selectedMfaMethod?: string;
|
||||||
|
allowSecretSharingOutsideOrganization?: boolean;
|
||||||
|
bypassOrgAuthEnabled?: boolean;
|
||||||
|
userTokenExpiration?: string;
|
||||||
|
secretsProductEnabled?: boolean;
|
||||||
|
pkiProductEnabled?: boolean;
|
||||||
|
kmsProductEnabled?: boolean;
|
||||||
|
sshProductEnabled?: boolean;
|
||||||
|
scannerProductEnabled?: boolean;
|
||||||
|
shareSecretsProductEnabled?: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProjectCreateEvent {
|
||||||
|
type: EventType.CREATE_PROJECT;
|
||||||
|
metadata: {
|
||||||
|
name: string;
|
||||||
|
slug?: string;
|
||||||
|
type: ProjectType;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProjectUpdateEvent {
|
||||||
|
type: EventType.UPDATE_PROJECT;
|
||||||
|
metadata: {
|
||||||
|
name?: string;
|
||||||
|
description?: string;
|
||||||
|
autoCapitalization?: boolean;
|
||||||
|
hasDeleteProtection?: boolean;
|
||||||
|
slug?: string;
|
||||||
|
secretSharing?: boolean;
|
||||||
|
pitVersionLimit?: number;
|
||||||
|
auditLogsRetentionDays?: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProjectDeleteEvent {
|
||||||
|
type: EventType.DELETE_PROJECT;
|
||||||
|
metadata: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export type Event =
|
export type Event =
|
||||||
| GetSecretsEvent
|
| GetSecretsEvent
|
||||||
| GetSecretEvent
|
| GetSecretEvent
|
||||||
@@ -3158,6 +3251,7 @@ export type Event =
|
|||||||
| IssueSshHostHostCert
|
| IssueSshHostHostCert
|
||||||
| CreateCa
|
| CreateCa
|
||||||
| GetCa
|
| GetCa
|
||||||
|
| GetCAs
|
||||||
| UpdateCa
|
| UpdateCa
|
||||||
| DeleteCa
|
| DeleteCa
|
||||||
| RenewCa
|
| RenewCa
|
||||||
@@ -3168,6 +3262,7 @@ export type Event =
|
|||||||
| ImportCaCert
|
| ImportCaCert
|
||||||
| GetCaCrls
|
| GetCaCrls
|
||||||
| IssueCert
|
| IssueCert
|
||||||
|
| ImportCert
|
||||||
| SignCert
|
| SignCert
|
||||||
| GetCaCertificateTemplates
|
| GetCaCertificateTemplates
|
||||||
| GetCert
|
| GetCert
|
||||||
@@ -3193,7 +3288,9 @@ export type Event =
|
|||||||
| GetPkiSubscriber
|
| GetPkiSubscriber
|
||||||
| IssuePkiSubscriberCert
|
| IssuePkiSubscriberCert
|
||||||
| SignPkiSubscriberCert
|
| SignPkiSubscriberCert
|
||||||
|
| AutomatedRenewPkiSubscriberCert
|
||||||
| ListPkiSubscriberCerts
|
| ListPkiSubscriberCerts
|
||||||
|
| GetSubscriberActiveCertBundle
|
||||||
| CreateKmsEvent
|
| CreateKmsEvent
|
||||||
| UpdateKmsEvent
|
| UpdateKmsEvent
|
||||||
| DeleteKmsEvent
|
| DeleteKmsEvent
|
||||||
@@ -3238,7 +3335,6 @@ export type Event =
|
|||||||
| CreateProjectTemplateEvent
|
| CreateProjectTemplateEvent
|
||||||
| UpdateProjectTemplateEvent
|
| UpdateProjectTemplateEvent
|
||||||
| DeleteProjectTemplateEvent
|
| DeleteProjectTemplateEvent
|
||||||
| ApplyProjectTemplateEvent
|
|
||||||
| GetAppConnectionsEvent
|
| GetAppConnectionsEvent
|
||||||
| GetAvailableAppConnectionsDetailsEvent
|
| GetAvailableAppConnectionsDetailsEvent
|
||||||
| GetAppConnectionEvent
|
| GetAppConnectionEvent
|
||||||
@@ -3313,4 +3409,8 @@ export type Event =
|
|||||||
| SecretScanningFindingListEvent
|
| SecretScanningFindingListEvent
|
||||||
| SecretScanningFindingUpdateEvent
|
| SecretScanningFindingUpdateEvent
|
||||||
| SecretScanningConfigUpdateEvent
|
| SecretScanningConfigUpdateEvent
|
||||||
| SecretScanningConfigReadEvent;
|
| SecretScanningConfigReadEvent
|
||||||
|
| OrgUpdateEvent
|
||||||
|
| ProjectCreateEvent
|
||||||
|
| ProjectUpdateEvent
|
||||||
|
| ProjectDeleteEvent;
|
||||||
|
|||||||
+8
-6
@@ -7,6 +7,7 @@ import { TPermissionServiceFactory } from "@app/ee/services/permission/permissio
|
|||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
import { NotFoundError } from "@app/lib/errors";
|
import { NotFoundError } from "@app/lib/errors";
|
||||||
import { TCertificateAuthorityDALFactory } from "@app/services/certificate-authority/certificate-authority-dal";
|
import { TCertificateAuthorityDALFactory } from "@app/services/certificate-authority/certificate-authority-dal";
|
||||||
|
import { expandInternalCa } from "@app/services/certificate-authority/certificate-authority-fns";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
||||||
@@ -14,7 +15,7 @@ import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns
|
|||||||
import { TGetCaCrlsDTO, TGetCrlById } from "./certificate-authority-crl-types";
|
import { TGetCaCrlsDTO, TGetCrlById } from "./certificate-authority-crl-types";
|
||||||
|
|
||||||
type TCertificateAuthorityCrlServiceFactoryDep = {
|
type TCertificateAuthorityCrlServiceFactoryDep = {
|
||||||
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
|
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findByIdWithAssociatedCa">;
|
||||||
certificateAuthorityCrlDAL: Pick<TCertificateAuthorityCrlDALFactory, "find" | "findById">;
|
certificateAuthorityCrlDAL: Pick<TCertificateAuthorityCrlDALFactory, "find" | "findById">;
|
||||||
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
|
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
|
||||||
kmsService: Pick<TKmsServiceFactory, "decryptWithKmsKey" | "generateKmsKey">;
|
kmsService: Pick<TKmsServiceFactory, "decryptWithKmsKey" | "generateKmsKey">;
|
||||||
@@ -37,7 +38,8 @@ export const certificateAuthorityCrlServiceFactory = ({
|
|||||||
const caCrl = await certificateAuthorityCrlDAL.findById(crlId);
|
const caCrl = await certificateAuthorityCrlDAL.findById(crlId);
|
||||||
if (!caCrl) throw new NotFoundError({ message: `CRL with ID '${crlId}' not found` });
|
if (!caCrl) throw new NotFoundError({ message: `CRL with ID '${crlId}' not found` });
|
||||||
|
|
||||||
const ca = await certificateAuthorityDAL.findById(caCrl.caId);
|
const ca = await certificateAuthorityDAL.findByIdWithAssociatedCa(caCrl.caId);
|
||||||
|
if (!ca?.internalCa?.id) throw new NotFoundError({ message: `Internal CA with ID '${caCrl.caId}' not found` });
|
||||||
|
|
||||||
const keyId = await getProjectKmsCertificateKeyId({
|
const keyId = await getProjectKmsCertificateKeyId({
|
||||||
projectId: ca.projectId,
|
projectId: ca.projectId,
|
||||||
@@ -54,7 +56,7 @@ export const certificateAuthorityCrlServiceFactory = ({
|
|||||||
const crl = new x509.X509Crl(decryptedCrl);
|
const crl = new x509.X509Crl(decryptedCrl);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ca,
|
ca: expandInternalCa(ca),
|
||||||
caCrl,
|
caCrl,
|
||||||
crl: crl.rawData
|
crl: crl.rawData
|
||||||
};
|
};
|
||||||
@@ -64,8 +66,8 @@ export const certificateAuthorityCrlServiceFactory = ({
|
|||||||
* Returns a list of CRL ids for CA with id [caId]
|
* Returns a list of CRL ids for CA with id [caId]
|
||||||
*/
|
*/
|
||||||
const getCaCrls = async ({ caId, actorId, actorAuthMethod, actor, actorOrgId }: TGetCaCrlsDTO) => {
|
const getCaCrls = async ({ caId, actorId, actorAuthMethod, actor, actorOrgId }: TGetCaCrlsDTO) => {
|
||||||
const ca = await certificateAuthorityDAL.findById(caId);
|
const ca = await certificateAuthorityDAL.findByIdWithAssociatedCa(caId);
|
||||||
if (!ca) throw new NotFoundError({ message: `CA with ID '${caId}' not found` });
|
if (!ca?.internalCa?.id) throw new NotFoundError({ message: `Internal CA with ID '${caId}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
@@ -108,7 +110,7 @@ export const certificateAuthorityCrlServiceFactory = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ca,
|
ca: expandInternalCa(ca),
|
||||||
crls: decryptedCrls
|
crls: decryptedCrls
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { isCertChainValid } from "@app/services/certificate/certificate-fns";
|
|||||||
import { TCertificateAuthorityCertDALFactory } from "@app/services/certificate-authority/certificate-authority-cert-dal";
|
import { TCertificateAuthorityCertDALFactory } from "@app/services/certificate-authority/certificate-authority-cert-dal";
|
||||||
import { TCertificateAuthorityDALFactory } from "@app/services/certificate-authority/certificate-authority-dal";
|
import { TCertificateAuthorityDALFactory } from "@app/services/certificate-authority/certificate-authority-dal";
|
||||||
import { getCaCertChain, getCaCertChains } from "@app/services/certificate-authority/certificate-authority-fns";
|
import { getCaCertChain, getCaCertChains } from "@app/services/certificate-authority/certificate-authority-fns";
|
||||||
import { TCertificateAuthorityServiceFactory } from "@app/services/certificate-authority/certificate-authority-service";
|
import { TInternalCertificateAuthorityServiceFactory } from "@app/services/certificate-authority/internal/internal-certificate-authority-service";
|
||||||
import { TCertificateTemplateDALFactory } from "@app/services/certificate-template/certificate-template-dal";
|
import { TCertificateTemplateDALFactory } from "@app/services/certificate-template/certificate-template-dal";
|
||||||
import { TCertificateTemplateServiceFactory } from "@app/services/certificate-template/certificate-template-service";
|
import { TCertificateTemplateServiceFactory } from "@app/services/certificate-template/certificate-template-service";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
@@ -16,10 +16,10 @@ import { TLicenseServiceFactory } from "../license/license-service";
|
|||||||
import { convertRawCertsToPkcs7 } from "./certificate-est-fns";
|
import { convertRawCertsToPkcs7 } from "./certificate-est-fns";
|
||||||
|
|
||||||
type TCertificateEstServiceFactoryDep = {
|
type TCertificateEstServiceFactoryDep = {
|
||||||
certificateAuthorityService: Pick<TCertificateAuthorityServiceFactory, "signCertFromCa">;
|
internalCertificateAuthorityService: Pick<TInternalCertificateAuthorityServiceFactory, "signCertFromCa">;
|
||||||
certificateTemplateService: Pick<TCertificateTemplateServiceFactory, "getEstConfiguration">;
|
certificateTemplateService: Pick<TCertificateTemplateServiceFactory, "getEstConfiguration">;
|
||||||
certificateTemplateDAL: Pick<TCertificateTemplateDALFactory, "findById">;
|
certificateTemplateDAL: Pick<TCertificateTemplateDALFactory, "findById">;
|
||||||
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
|
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById" | "findByIdWithAssociatedCa">;
|
||||||
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "find" | "findById">;
|
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "find" | "findById">;
|
||||||
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
|
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
|
||||||
kmsService: Pick<TKmsServiceFactory, "decryptWithKmsKey" | "generateKmsKey">;
|
kmsService: Pick<TKmsServiceFactory, "decryptWithKmsKey" | "generateKmsKey">;
|
||||||
@@ -29,7 +29,7 @@ type TCertificateEstServiceFactoryDep = {
|
|||||||
export type TCertificateEstServiceFactory = ReturnType<typeof certificateEstServiceFactory>;
|
export type TCertificateEstServiceFactory = ReturnType<typeof certificateEstServiceFactory>;
|
||||||
|
|
||||||
export const certificateEstServiceFactory = ({
|
export const certificateEstServiceFactory = ({
|
||||||
certificateAuthorityService,
|
internalCertificateAuthorityService,
|
||||||
certificateTemplateService,
|
certificateTemplateService,
|
||||||
certificateTemplateDAL,
|
certificateTemplateDAL,
|
||||||
certificateAuthorityCertDAL,
|
certificateAuthorityCertDAL,
|
||||||
@@ -127,7 +127,7 @@ export const certificateEstServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { certificate } = await certificateAuthorityService.signCertFromCa({
|
const { certificate } = await internalCertificateAuthorityService.signCertFromCa({
|
||||||
isInternal: true,
|
isInternal: true,
|
||||||
certificateTemplateId,
|
certificateTemplateId,
|
||||||
csr
|
csr
|
||||||
@@ -188,7 +188,7 @@ export const certificateEstServiceFactory = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { certificate } = await certificateAuthorityService.signCertFromCa({
|
const { certificate } = await internalCertificateAuthorityService.signCertFromCa({
|
||||||
isInternal: true,
|
isInternal: true,
|
||||||
certificateTemplateId,
|
certificateTemplateId,
|
||||||
csr
|
csr
|
||||||
@@ -227,15 +227,15 @@ export const certificateEstServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const ca = await certificateAuthorityDAL.findById(certTemplate.caId);
|
const ca = await certificateAuthorityDAL.findByIdWithAssociatedCa(certTemplate.caId);
|
||||||
if (!ca) {
|
if (!ca?.internalCa?.id) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Certificate Authority with ID '${certTemplate.caId}' not found`
|
message: `Internal Certificate Authority with ID '${certTemplate.caId}' not found`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { caCert, caCertChain } = await getCaCertChain({
|
const { caCert, caCertChain } = await getCaCertChain({
|
||||||
caCertId: ca.activeCaCertId as string,
|
caCertId: ca.internalCa.activeCaCertId as string,
|
||||||
certificateAuthorityDAL,
|
certificateAuthorityDAL,
|
||||||
certificateAuthorityCertDAL,
|
certificateAuthorityCertDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
|
|||||||
@@ -132,7 +132,11 @@ export const dynamicSecretLeaseServiceFactory = ({
|
|||||||
|
|
||||||
let result;
|
let result;
|
||||||
try {
|
try {
|
||||||
result = await selectedProvider.create(decryptedStoredInput, expireAt.getTime());
|
result = await selectedProvider.create({
|
||||||
|
inputs: decryptedStoredInput,
|
||||||
|
expireAt: expireAt.getTime(),
|
||||||
|
usernameTemplate: dynamicSecretCfg.usernameTemplate
|
||||||
|
});
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
if (error && typeof error === "object" && error !== null && "sqlMessage" in error) {
|
if (error && typeof error === "object" && error !== null && "sqlMessage" in error) {
|
||||||
throw new BadRequestError({ message: error.sqlMessage as string });
|
throw new BadRequestError({ message: error.sqlMessage as string });
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ export const verifyHostInputValidity = async (host: string, isGateway = false) =
|
|||||||
|
|
||||||
if (appCfg.isDevelopmentMode) return [host];
|
if (appCfg.isDevelopmentMode) return [host];
|
||||||
|
|
||||||
|
if (isGateway) return [host];
|
||||||
|
|
||||||
const reservedHosts = [appCfg.DB_HOST || getDbConnectionHost(appCfg.DB_CONNECTION_URI)].concat(
|
const reservedHosts = [appCfg.DB_HOST || getDbConnectionHost(appCfg.DB_CONNECTION_URI)].concat(
|
||||||
(appCfg.DB_READ_REPLICAS || []).map((el) => getDbConnectionHost(el.DB_CONNECTION_URI)),
|
(appCfg.DB_READ_REPLICAS || []).map((el) => getDbConnectionHost(el.DB_CONNECTION_URI)),
|
||||||
getDbConnectionHost(appCfg.REDIS_URL),
|
getDbConnectionHost(appCfg.REDIS_URL),
|
||||||
@@ -58,7 +60,7 @@ export const verifyHostInputValidity = async (host: string, isGateway = false) =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isGateway && !(appCfg.DYNAMIC_SECRET_ALLOW_INTERNAL_IP || appCfg.ALLOW_INTERNAL_IP_CONNECTIONS)) {
|
if (!(appCfg.DYNAMIC_SECRET_ALLOW_INTERNAL_IP || appCfg.ALLOW_INTERNAL_IP_CONNECTIONS)) {
|
||||||
const isInternalIp = inputHostIps.some((el) => isPrivateIp(el));
|
const isInternalIp = inputHostIps.some((el) => isPrivateIp(el));
|
||||||
if (isInternalIp) throw new BadRequestError({ message: "Invalid db host" });
|
if (isInternalIp) throw new BadRequestError({ message: "Invalid db host" });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,8 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
actorOrgId,
|
actorOrgId,
|
||||||
defaultTTL,
|
defaultTTL,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
metadata
|
metadata,
|
||||||
|
usernameTemplate
|
||||||
}: TCreateDynamicSecretDTO) => {
|
}: TCreateDynamicSecretDTO) => {
|
||||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
||||||
@@ -163,7 +164,8 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
defaultTTL,
|
defaultTTL,
|
||||||
folderId: folder.id,
|
folderId: folder.id,
|
||||||
name,
|
name,
|
||||||
gatewayId: selectedGatewayId
|
gatewayId: selectedGatewayId,
|
||||||
|
usernameTemplate
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@@ -199,7 +201,8 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
newName,
|
newName,
|
||||||
actorOrgId,
|
actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
metadata
|
metadata,
|
||||||
|
usernameTemplate
|
||||||
}: TUpdateDynamicSecretDTO) => {
|
}: TUpdateDynamicSecretDTO) => {
|
||||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
||||||
@@ -311,7 +314,8 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
defaultTTL,
|
defaultTTL,
|
||||||
name: newName ?? name,
|
name: newName ?? name,
|
||||||
status: null,
|
status: null,
|
||||||
gatewayId: selectedGatewayId
|
gatewayId: selectedGatewayId,
|
||||||
|
usernameTemplate
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export type TCreateDynamicSecretDTO = {
|
|||||||
name: string;
|
name: string;
|
||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
metadata?: ResourceMetadataDTO;
|
metadata?: ResourceMetadataDTO;
|
||||||
|
usernameTemplate?: string | null;
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
export type TUpdateDynamicSecretDTO = {
|
export type TUpdateDynamicSecretDTO = {
|
||||||
@@ -34,6 +35,7 @@ export type TUpdateDynamicSecretDTO = {
|
|||||||
inputs?: TProvider["inputs"];
|
inputs?: TProvider["inputs"];
|
||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
metadata?: ResourceMetadataDTO;
|
metadata?: ResourceMetadataDTO;
|
||||||
|
usernameTemplate?: string | null;
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
export type TDeleteDynamicSecretDTO = {
|
export type TDeleteDynamicSecretDTO = {
|
||||||
|
|||||||
@@ -132,9 +132,15 @@ const generatePassword = () => {
|
|||||||
return customAlphabet(charset, 64)();
|
return customAlphabet(charset, 64)();
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUsername = () => {
|
const generateUsername = (usernameTemplate?: string | null) => {
|
||||||
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-";
|
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-";
|
||||||
return `inf-${customAlphabet(charset, 32)()}`; // Username must start with an ascii letter, so we prepend the username with "inf-"
|
const randomUsername = `inf-${customAlphabet(charset, 32)()}`;
|
||||||
|
if (!usernameTemplate) return randomUsername;
|
||||||
|
|
||||||
|
return handlebars.compile(usernameTemplate)({
|
||||||
|
randomUsername,
|
||||||
|
unixTimestamp: Math.floor(Date.now() / 100)
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => {
|
export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => {
|
||||||
@@ -168,13 +174,14 @@ export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (inputs: unknown, expireAt: number) => {
|
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => {
|
||||||
|
const { inputs, expireAt, usernameTemplate } = data;
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
if (!(await validateConnection(providerInputs))) {
|
if (!(await validateConnection(providerInputs))) {
|
||||||
throw new BadRequestError({ message: "Failed to establish connection" });
|
throw new BadRequestError({ message: "Failed to establish connection" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const leaseUsername = generateUsername();
|
const leaseUsername = generateUsername(usernameTemplate);
|
||||||
const leasePassword = generatePassword();
|
const leasePassword = generatePassword();
|
||||||
const leaseExpiration = new Date(expireAt).toISOString();
|
const leaseExpiration = new Date(expireAt).toISOString();
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
PutUserPolicyCommand,
|
PutUserPolicyCommand,
|
||||||
RemoveUserFromGroupCommand
|
RemoveUserFromGroupCommand
|
||||||
} from "@aws-sdk/client-iam";
|
} from "@aws-sdk/client-iam";
|
||||||
|
import handlebars from "handlebars";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
@@ -23,8 +24,14 @@ import { alphaNumericNanoId } from "@app/lib/nanoid";
|
|||||||
|
|
||||||
import { DynamicSecretAwsIamSchema, TDynamicProviderFns } from "./models";
|
import { DynamicSecretAwsIamSchema, TDynamicProviderFns } from "./models";
|
||||||
|
|
||||||
const generateUsername = () => {
|
const generateUsername = (usernameTemplate?: string | null) => {
|
||||||
return alphaNumericNanoId(32);
|
const randomUsername = alphaNumericNanoId(32);
|
||||||
|
if (!usernameTemplate) return randomUsername;
|
||||||
|
|
||||||
|
return handlebars.compile(usernameTemplate)({
|
||||||
|
randomUsername,
|
||||||
|
unixTimestamp: Math.floor(Date.now() / 100)
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AwsIamProvider = (): TDynamicProviderFns => {
|
export const AwsIamProvider = (): TDynamicProviderFns => {
|
||||||
@@ -53,11 +60,13 @@ export const AwsIamProvider = (): TDynamicProviderFns => {
|
|||||||
return isConnected;
|
return isConnected;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (inputs: unknown) => {
|
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => {
|
||||||
|
const { inputs, usernameTemplate } = data;
|
||||||
|
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
const client = await $getClient(providerInputs);
|
const client = await $getClient(providerInputs);
|
||||||
|
|
||||||
const username = generateUsername();
|
const username = generateUsername(usernameTemplate);
|
||||||
const { policyArns, userGroups, policyDocument, awsPath, permissionBoundaryPolicyArn } = providerInputs;
|
const { policyArns, userGroups, policyDocument, awsPath, permissionBoundaryPolicyArn } = providerInputs;
|
||||||
const createUserRes = await client.send(
|
const createUserRes = await client.send(
|
||||||
new CreateUserCommand({
|
new CreateUserCommand({
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export const AzureEntraIDProvider = (): TDynamicProviderFns & {
|
|||||||
return data.success;
|
return data.success;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (inputs: unknown) => {
|
const create = async ({ inputs }: { inputs: unknown }) => {
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
const data = await $getToken(providerInputs.tenantId, providerInputs.applicationId, providerInputs.clientSecret);
|
const data = await $getToken(providerInputs.tenantId, providerInputs.applicationId, providerInputs.clientSecret);
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
@@ -88,7 +88,7 @@ export const AzureEntraIDProvider = (): TDynamicProviderFns & {
|
|||||||
|
|
||||||
const revoke = async (inputs: unknown, entityId: string) => {
|
const revoke = async (inputs: unknown, entityId: string) => {
|
||||||
// Creates a new password
|
// Creates a new password
|
||||||
await create(inputs);
|
await create({ inputs });
|
||||||
return { entityId };
|
return { entityId };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,14 @@ const generatePassword = (size = 48) => {
|
|||||||
return customAlphabet(charset, 48)(size);
|
return customAlphabet(charset, 48)(size);
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUsername = () => {
|
const generateUsername = (usernameTemplate?: string | null) => {
|
||||||
return alphaNumericNanoId(32);
|
const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-"
|
||||||
|
if (!usernameTemplate) return randomUsername;
|
||||||
|
|
||||||
|
return handlebars.compile(usernameTemplate)({
|
||||||
|
randomUsername,
|
||||||
|
unixTimestamp: Math.floor(Date.now() / 100)
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CassandraProvider = (): TDynamicProviderFns => {
|
export const CassandraProvider = (): TDynamicProviderFns => {
|
||||||
@@ -69,11 +75,12 @@ export const CassandraProvider = (): TDynamicProviderFns => {
|
|||||||
return isConnected;
|
return isConnected;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (inputs: unknown, expireAt: number) => {
|
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => {
|
||||||
|
const { inputs, expireAt, usernameTemplate } = data;
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
const client = await $getClient(providerInputs);
|
const client = await $getClient(providerInputs);
|
||||||
|
|
||||||
const username = generateUsername();
|
const username = generateUsername(usernameTemplate);
|
||||||
const password = generatePassword();
|
const password = generatePassword();
|
||||||
const { keyspace } = providerInputs;
|
const { keyspace } = providerInputs;
|
||||||
const expiration = new Date(expireAt).toISOString();
|
const expiration = new Date(expireAt).toISOString();
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Client as ElasticSearchClient } from "@elastic/elasticsearch";
|
import { Client as ElasticSearchClient } from "@elastic/elasticsearch";
|
||||||
|
import handlebars from "handlebars";
|
||||||
import { customAlphabet } from "nanoid";
|
import { customAlphabet } from "nanoid";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
@@ -12,8 +13,14 @@ const generatePassword = () => {
|
|||||||
return customAlphabet(charset, 64)();
|
return customAlphabet(charset, 64)();
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUsername = () => {
|
const generateUsername = (usernameTemplate?: string | null) => {
|
||||||
return alphaNumericNanoId(32);
|
const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-"
|
||||||
|
if (!usernameTemplate) return randomUsername;
|
||||||
|
|
||||||
|
return handlebars.compile(usernameTemplate)({
|
||||||
|
randomUsername,
|
||||||
|
unixTimestamp: Math.floor(Date.now() / 100)
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ElasticSearchProvider = (): TDynamicProviderFns => {
|
export const ElasticSearchProvider = (): TDynamicProviderFns => {
|
||||||
@@ -64,11 +71,12 @@ export const ElasticSearchProvider = (): TDynamicProviderFns => {
|
|||||||
return infoResponse;
|
return infoResponse;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (inputs: unknown) => {
|
const create = async (data: { inputs: unknown; usernameTemplate?: string | null }) => {
|
||||||
|
const { inputs, usernameTemplate } = data;
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
const connection = await $getClient(providerInputs);
|
const connection = await $getClient(providerInputs);
|
||||||
|
|
||||||
const username = generateUsername();
|
const username = generateUsername(usernameTemplate);
|
||||||
const password = generatePassword();
|
const password = generatePassword();
|
||||||
|
|
||||||
await connection.security.putUser({
|
await connection.security.putUser({
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { AwsIamProvider } from "./aws-iam";
|
|||||||
import { AzureEntraIDProvider } from "./azure-entra-id";
|
import { AzureEntraIDProvider } from "./azure-entra-id";
|
||||||
import { CassandraProvider } from "./cassandra";
|
import { CassandraProvider } from "./cassandra";
|
||||||
import { ElasticSearchProvider } from "./elastic-search";
|
import { ElasticSearchProvider } from "./elastic-search";
|
||||||
|
import { KubernetesProvider } from "./kubernetes";
|
||||||
import { LdapProvider } from "./ldap";
|
import { LdapProvider } from "./ldap";
|
||||||
import { DynamicSecretProviders, TDynamicProviderFns } from "./models";
|
import { DynamicSecretProviders, TDynamicProviderFns } from "./models";
|
||||||
import { MongoAtlasProvider } from "./mongo-atlas";
|
import { MongoAtlasProvider } from "./mongo-atlas";
|
||||||
@@ -38,5 +39,6 @@ export const buildDynamicSecretProviders = ({
|
|||||||
[DynamicSecretProviders.SapHana]: SapHanaProvider(),
|
[DynamicSecretProviders.SapHana]: SapHanaProvider(),
|
||||||
[DynamicSecretProviders.Snowflake]: SnowflakeProvider(),
|
[DynamicSecretProviders.Snowflake]: SnowflakeProvider(),
|
||||||
[DynamicSecretProviders.Totp]: TotpProvider(),
|
[DynamicSecretProviders.Totp]: TotpProvider(),
|
||||||
[DynamicSecretProviders.SapAse]: SapAseProvider()
|
[DynamicSecretProviders.SapAse]: SapAseProvider(),
|
||||||
|
[DynamicSecretProviders.Kubernetes]: KubernetesProvider({ gatewayService })
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,199 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import https from "https";
|
||||||
|
|
||||||
|
import { InternalServerError } from "@app/lib/errors";
|
||||||
|
import { withGatewayProxy } from "@app/lib/gateway";
|
||||||
|
import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator";
|
||||||
|
import { TKubernetesTokenRequest } from "@app/services/identity-kubernetes-auth/identity-kubernetes-auth-types";
|
||||||
|
|
||||||
|
import { TGatewayServiceFactory } from "../../gateway/gateway-service";
|
||||||
|
import { DynamicSecretKubernetesSchema, TDynamicProviderFns } from "./models";
|
||||||
|
|
||||||
|
const EXTERNAL_REQUEST_TIMEOUT = 10 * 1000;
|
||||||
|
|
||||||
|
type TKubernetesProviderDTO = {
|
||||||
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const KubernetesProvider = ({ gatewayService }: TKubernetesProviderDTO): TDynamicProviderFns => {
|
||||||
|
const validateProviderInputs = async (inputs: unknown) => {
|
||||||
|
const providerInputs = await DynamicSecretKubernetesSchema.parseAsync(inputs);
|
||||||
|
if (!providerInputs.gatewayId) {
|
||||||
|
await blockLocalAndPrivateIpAddresses(providerInputs.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
return providerInputs;
|
||||||
|
};
|
||||||
|
|
||||||
|
const $gatewayProxyWrapper = async <T>(
|
||||||
|
inputs: {
|
||||||
|
gatewayId: string;
|
||||||
|
targetHost: string;
|
||||||
|
targetPort: number;
|
||||||
|
},
|
||||||
|
gatewayCallback: (host: string, port: number) => Promise<T>
|
||||||
|
): Promise<T> => {
|
||||||
|
const relayDetails = await gatewayService.fnGetGatewayClientTlsByGatewayId(inputs.gatewayId);
|
||||||
|
const [relayHost, relayPort] = relayDetails.relayAddress.split(":");
|
||||||
|
|
||||||
|
const callbackResult = await withGatewayProxy(
|
||||||
|
async (port) => {
|
||||||
|
// Needs to be https protocol or the kubernetes API server will fail with "Client sent an HTTP request to an HTTPS server"
|
||||||
|
const res = await gatewayCallback("https://localhost", port);
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
targetHost: inputs.targetHost,
|
||||||
|
targetPort: inputs.targetPort,
|
||||||
|
relayHost,
|
||||||
|
relayPort: Number(relayPort),
|
||||||
|
identityId: relayDetails.identityId,
|
||||||
|
orgId: relayDetails.orgId,
|
||||||
|
tlsOptions: {
|
||||||
|
ca: relayDetails.certChain,
|
||||||
|
cert: relayDetails.certificate,
|
||||||
|
key: relayDetails.privateKey.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return callbackResult;
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateConnection = async (inputs: unknown) => {
|
||||||
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
|
|
||||||
|
const serviceAccountGetCallback = async (host: string, port: number) => {
|
||||||
|
const baseUrl = port ? `${host}:${port}` : host;
|
||||||
|
|
||||||
|
await axios.get(
|
||||||
|
`${baseUrl}/api/v1/namespaces/${providerInputs.namespace}/serviceaccounts/${providerInputs.serviceAccountName}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: `Bearer ${providerInputs.clusterToken}`
|
||||||
|
},
|
||||||
|
signal: AbortSignal.timeout(EXTERNAL_REQUEST_TIMEOUT),
|
||||||
|
timeout: EXTERNAL_REQUEST_TIMEOUT,
|
||||||
|
httpsAgent: new https.Agent({
|
||||||
|
ca: providerInputs.ca,
|
||||||
|
rejectUnauthorized: providerInputs.sslEnabled
|
||||||
|
})
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const url = new URL(providerInputs.url);
|
||||||
|
const k8sPort = url.port ? Number(url.port) : 443;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (providerInputs.gatewayId) {
|
||||||
|
const k8sHost = url.hostname;
|
||||||
|
|
||||||
|
await $gatewayProxyWrapper(
|
||||||
|
{
|
||||||
|
gatewayId: providerInputs.gatewayId,
|
||||||
|
targetHost: k8sHost,
|
||||||
|
targetPort: k8sPort
|
||||||
|
},
|
||||||
|
serviceAccountGetCallback
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const k8sHost = `${url.protocol}//${url.hostname}`;
|
||||||
|
await serviceAccountGetCallback(k8sHost, k8sPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
let errorMessage = error instanceof Error ? error.message : "Unknown error";
|
||||||
|
if (axios.isAxiosError(error) && (error.response?.data as { message: string })?.message) {
|
||||||
|
errorMessage = (error.response?.data as { message: string }).message;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InternalServerError({
|
||||||
|
message: `Failed to validate connection: ${errorMessage}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const create = async ({ inputs, expireAt }: { inputs: unknown; expireAt: number }) => {
|
||||||
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
|
|
||||||
|
const tokenRequestCallback = async (host: string, port: number) => {
|
||||||
|
const baseUrl = port ? `${host}:${port}` : host;
|
||||||
|
|
||||||
|
const res = await axios.post<TKubernetesTokenRequest>(
|
||||||
|
`${baseUrl}/api/v1/namespaces/${providerInputs.namespace}/serviceaccounts/${providerInputs.serviceAccountName}/token`,
|
||||||
|
{
|
||||||
|
spec: {
|
||||||
|
expirationSeconds: Math.floor((expireAt - Date.now()) / 1000),
|
||||||
|
...(providerInputs.audiences?.length ? { audiences: providerInputs.audiences } : {})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: `Bearer ${providerInputs.clusterToken}`
|
||||||
|
},
|
||||||
|
signal: AbortSignal.timeout(EXTERNAL_REQUEST_TIMEOUT),
|
||||||
|
timeout: EXTERNAL_REQUEST_TIMEOUT,
|
||||||
|
httpsAgent: new https.Agent({
|
||||||
|
ca: providerInputs.ca,
|
||||||
|
rejectUnauthorized: providerInputs.sslEnabled
|
||||||
|
})
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return res.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const url = new URL(providerInputs.url);
|
||||||
|
const k8sHost = `${url.protocol}//${url.hostname}`;
|
||||||
|
const k8sGatewayHost = url.hostname;
|
||||||
|
const k8sPort = url.port ? Number(url.port) : 443;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const tokenData = providerInputs.gatewayId
|
||||||
|
? await $gatewayProxyWrapper(
|
||||||
|
{
|
||||||
|
gatewayId: providerInputs.gatewayId,
|
||||||
|
targetHost: k8sGatewayHost,
|
||||||
|
targetPort: k8sPort
|
||||||
|
},
|
||||||
|
tokenRequestCallback
|
||||||
|
)
|
||||||
|
: await tokenRequestCallback(k8sHost, k8sPort);
|
||||||
|
|
||||||
|
return {
|
||||||
|
entityId: providerInputs.serviceAccountName,
|
||||||
|
data: { TOKEN: tokenData.status.token }
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
let errorMessage = error instanceof Error ? error.message : "Unknown error";
|
||||||
|
if (axios.isAxiosError(error) && (error.response?.data as { message: string })?.message) {
|
||||||
|
errorMessage = (error.response?.data as { message: string }).message;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InternalServerError({
|
||||||
|
message: `Failed to create dynamic secret: ${errorMessage}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const revoke = async (_inputs: unknown, entityId: string) => {
|
||||||
|
return { entityId };
|
||||||
|
};
|
||||||
|
|
||||||
|
const renew = async (_inputs: unknown, entityId: string) => {
|
||||||
|
// No renewal necessary
|
||||||
|
return { entityId };
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
validateProviderInputs,
|
||||||
|
validateConnection,
|
||||||
|
create,
|
||||||
|
revoke,
|
||||||
|
renew
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -22,8 +22,14 @@ const encodePassword = (password?: string) => {
|
|||||||
return base64Password;
|
return base64Password;
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUsername = () => {
|
const generateUsername = (usernameTemplate?: string | null) => {
|
||||||
return alphaNumericNanoId(20);
|
const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-"
|
||||||
|
if (!usernameTemplate) return randomUsername;
|
||||||
|
|
||||||
|
return handlebars.compile(usernameTemplate)({
|
||||||
|
randomUsername,
|
||||||
|
unixTimestamp: Math.floor(Date.now() / 100)
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateLDIF = ({
|
const generateLDIF = ({
|
||||||
@@ -190,7 +196,8 @@ export const LdapProvider = (): TDynamicProviderFns => {
|
|||||||
return dnArray;
|
return dnArray;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (inputs: unknown) => {
|
const create = async (data: { inputs: unknown; usernameTemplate?: string | null }) => {
|
||||||
|
const { inputs, usernameTemplate } = data;
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
const client = await $getClient(providerInputs);
|
const client = await $getClient(providerInputs);
|
||||||
|
|
||||||
@@ -217,7 +224,7 @@ export const LdapProvider = (): TDynamicProviderFns => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const username = generateUsername();
|
const username = generateUsername(usernameTemplate);
|
||||||
const password = generatePassword();
|
const password = generatePassword();
|
||||||
const generatedLdif = generateLDIF({ username, password, ldifTemplate: providerInputs.creationLdif });
|
const generatedLdif = generateLDIF({ username, password, ldifTemplate: providerInputs.creationLdif });
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ export enum LdapCredentialType {
|
|||||||
Static = "static"
|
Static = "static"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum KubernetesCredentialType {
|
||||||
|
Static = "static"
|
||||||
|
}
|
||||||
|
|
||||||
export enum TotpConfigType {
|
export enum TotpConfigType {
|
||||||
URL = "url",
|
URL = "url",
|
||||||
MANUAL = "manual"
|
MANUAL = "manual"
|
||||||
@@ -277,6 +281,18 @@ export const LdapSchema = z.union([
|
|||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
export const DynamicSecretKubernetesSchema = z.object({
|
||||||
|
url: z.string().url().trim().min(1),
|
||||||
|
gatewayId: z.string().nullable().optional(),
|
||||||
|
sslEnabled: z.boolean().default(true),
|
||||||
|
clusterToken: z.string().trim().min(1),
|
||||||
|
ca: z.string().optional(),
|
||||||
|
serviceAccountName: z.string().trim().min(1),
|
||||||
|
credentialType: z.literal(KubernetesCredentialType.Static),
|
||||||
|
namespace: z.string().trim().min(1),
|
||||||
|
audiences: z.array(z.string().trim().min(1))
|
||||||
|
});
|
||||||
|
|
||||||
export const DynamicSecretTotpSchema = z.discriminatedUnion("configType", [
|
export const DynamicSecretTotpSchema = z.discriminatedUnion("configType", [
|
||||||
z.object({
|
z.object({
|
||||||
configType: z.literal(TotpConfigType.URL),
|
configType: z.literal(TotpConfigType.URL),
|
||||||
@@ -320,7 +336,8 @@ export enum DynamicSecretProviders {
|
|||||||
SapHana = "sap-hana",
|
SapHana = "sap-hana",
|
||||||
Snowflake = "snowflake",
|
Snowflake = "snowflake",
|
||||||
Totp = "totp",
|
Totp = "totp",
|
||||||
SapAse = "sap-ase"
|
SapAse = "sap-ase",
|
||||||
|
Kubernetes = "kubernetes"
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DynamicSecretProviderSchema = z.discriminatedUnion("type", [
|
export const DynamicSecretProviderSchema = z.discriminatedUnion("type", [
|
||||||
@@ -338,11 +355,16 @@ export const DynamicSecretProviderSchema = z.discriminatedUnion("type", [
|
|||||||
z.object({ type: z.literal(DynamicSecretProviders.AzureEntraID), inputs: AzureEntraIDSchema }),
|
z.object({ type: z.literal(DynamicSecretProviders.AzureEntraID), inputs: AzureEntraIDSchema }),
|
||||||
z.object({ type: z.literal(DynamicSecretProviders.Ldap), inputs: LdapSchema }),
|
z.object({ type: z.literal(DynamicSecretProviders.Ldap), inputs: LdapSchema }),
|
||||||
z.object({ type: z.literal(DynamicSecretProviders.Snowflake), inputs: DynamicSecretSnowflakeSchema }),
|
z.object({ type: z.literal(DynamicSecretProviders.Snowflake), inputs: DynamicSecretSnowflakeSchema }),
|
||||||
z.object({ type: z.literal(DynamicSecretProviders.Totp), inputs: DynamicSecretTotpSchema })
|
z.object({ type: z.literal(DynamicSecretProviders.Totp), inputs: DynamicSecretTotpSchema }),
|
||||||
|
z.object({ type: z.literal(DynamicSecretProviders.Kubernetes), inputs: DynamicSecretKubernetesSchema })
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export type TDynamicProviderFns = {
|
export type TDynamicProviderFns = {
|
||||||
create: (inputs: unknown, expireAt: number) => Promise<{ entityId: string; data: unknown }>;
|
create: (arg: {
|
||||||
|
inputs: unknown;
|
||||||
|
expireAt: number;
|
||||||
|
usernameTemplate?: string | null;
|
||||||
|
}) => Promise<{ entityId: string; data: unknown }>;
|
||||||
validateConnection: (inputs: unknown) => Promise<boolean>;
|
validateConnection: (inputs: unknown) => Promise<boolean>;
|
||||||
validateProviderInputs: (inputs: object) => Promise<unknown>;
|
validateProviderInputs: (inputs: object) => Promise<unknown>;
|
||||||
revoke: (inputs: unknown, entityId: string) => Promise<{ entityId: string }>;
|
revoke: (inputs: unknown, entityId: string) => Promise<{ entityId: string }>;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import axios, { AxiosError } from "axios";
|
import axios, { AxiosError } from "axios";
|
||||||
|
import handlebars from "handlebars";
|
||||||
import { customAlphabet } from "nanoid";
|
import { customAlphabet } from "nanoid";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
@@ -12,8 +13,14 @@ const generatePassword = (size = 48) => {
|
|||||||
return customAlphabet(charset, 48)(size);
|
return customAlphabet(charset, 48)(size);
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUsername = () => {
|
const generateUsername = (usernameTemplate?: string | null) => {
|
||||||
return alphaNumericNanoId(32);
|
const randomUsername = alphaNumericNanoId(32);
|
||||||
|
if (!usernameTemplate) return randomUsername;
|
||||||
|
|
||||||
|
return handlebars.compile(usernameTemplate)({
|
||||||
|
randomUsername,
|
||||||
|
unixTimestamp: Math.floor(Date.now() / 100)
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MongoAtlasProvider = (): TDynamicProviderFns => {
|
export const MongoAtlasProvider = (): TDynamicProviderFns => {
|
||||||
@@ -57,11 +64,12 @@ export const MongoAtlasProvider = (): TDynamicProviderFns => {
|
|||||||
return isConnected;
|
return isConnected;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (inputs: unknown, expireAt: number) => {
|
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => {
|
||||||
|
const { inputs, expireAt, usernameTemplate } = data;
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
const client = await $getClient(providerInputs);
|
const client = await $getClient(providerInputs);
|
||||||
|
|
||||||
const username = generateUsername();
|
const username = generateUsername(usernameTemplate);
|
||||||
const password = generatePassword();
|
const password = generatePassword();
|
||||||
const expiration = new Date(expireAt).toISOString();
|
const expiration = new Date(expireAt).toISOString();
|
||||||
await client({
|
await client({
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import handlebars from "handlebars";
|
||||||
import { MongoClient } from "mongodb";
|
import { MongoClient } from "mongodb";
|
||||||
import { customAlphabet } from "nanoid";
|
import { customAlphabet } from "nanoid";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@@ -12,8 +13,14 @@ const generatePassword = (size = 48) => {
|
|||||||
return customAlphabet(charset, 48)(size);
|
return customAlphabet(charset, 48)(size);
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUsername = () => {
|
const generateUsername = (usernameTemplate?: string | null) => {
|
||||||
return alphaNumericNanoId(32);
|
const randomUsername = alphaNumericNanoId(32);
|
||||||
|
if (!usernameTemplate) return randomUsername;
|
||||||
|
|
||||||
|
return handlebars.compile(usernameTemplate)({
|
||||||
|
randomUsername,
|
||||||
|
unixTimestamp: Math.floor(Date.now() / 100)
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MongoDBProvider = (): TDynamicProviderFns => {
|
export const MongoDBProvider = (): TDynamicProviderFns => {
|
||||||
@@ -53,11 +60,12 @@ export const MongoDBProvider = (): TDynamicProviderFns => {
|
|||||||
return isConnected;
|
return isConnected;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (inputs: unknown) => {
|
const create = async (data: { inputs: unknown; usernameTemplate?: string | null }) => {
|
||||||
|
const { inputs, usernameTemplate } = data;
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
const client = await $getClient(providerInputs);
|
const client = await $getClient(providerInputs);
|
||||||
|
|
||||||
const username = generateUsername();
|
const username = generateUsername(usernameTemplate);
|
||||||
const password = generatePassword();
|
const password = generatePassword();
|
||||||
|
|
||||||
const db = client.db(providerInputs.database);
|
const db = client.db(providerInputs.database);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import axios, { Axios } from "axios";
|
import axios, { Axios } from "axios";
|
||||||
|
import handlebars from "handlebars";
|
||||||
import https from "https";
|
import https from "https";
|
||||||
import { customAlphabet } from "nanoid";
|
import { customAlphabet } from "nanoid";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@@ -14,8 +15,14 @@ const generatePassword = () => {
|
|||||||
return customAlphabet(charset, 64)();
|
return customAlphabet(charset, 64)();
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUsername = () => {
|
const generateUsername = (usernameTemplate?: string | null) => {
|
||||||
return alphaNumericNanoId(32);
|
const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-"
|
||||||
|
if (!usernameTemplate) return randomUsername;
|
||||||
|
|
||||||
|
return handlebars.compile(usernameTemplate)({
|
||||||
|
randomUsername,
|
||||||
|
unixTimestamp: Math.floor(Date.now() / 100)
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
type TCreateRabbitMQUser = {
|
type TCreateRabbitMQUser = {
|
||||||
@@ -110,11 +117,12 @@ export const RabbitMqProvider = (): TDynamicProviderFns => {
|
|||||||
return infoResponse;
|
return infoResponse;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (inputs: unknown) => {
|
const create = async (data: { inputs: unknown; usernameTemplate?: string | null }) => {
|
||||||
|
const { inputs, usernameTemplate } = data;
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
const connection = await $getClient(providerInputs);
|
const connection = await $getClient(providerInputs);
|
||||||
|
|
||||||
const username = generateUsername();
|
const username = generateUsername(usernameTemplate);
|
||||||
const password = generatePassword();
|
const password = generatePassword();
|
||||||
|
|
||||||
await createRabbitMqUser({
|
await createRabbitMqUser({
|
||||||
|
|||||||
@@ -15,8 +15,14 @@ const generatePassword = () => {
|
|||||||
return customAlphabet(charset, 64)();
|
return customAlphabet(charset, 64)();
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUsername = () => {
|
const generateUsername = (usernameTemplate?: string | null) => {
|
||||||
return alphaNumericNanoId(32);
|
const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-"
|
||||||
|
if (!usernameTemplate) return randomUsername;
|
||||||
|
|
||||||
|
return handlebars.compile(usernameTemplate)({
|
||||||
|
randomUsername,
|
||||||
|
unixTimestamp: Math.floor(Date.now() / 100)
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const executeTransactions = async (connection: Redis, commands: string[]): Promise<(string | null)[] | null> => {
|
const executeTransactions = async (connection: Redis, commands: string[]): Promise<(string | null)[] | null> => {
|
||||||
@@ -115,11 +121,12 @@ export const RedisDatabaseProvider = (): TDynamicProviderFns => {
|
|||||||
return pingResponse;
|
return pingResponse;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (inputs: unknown, expireAt: number) => {
|
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => {
|
||||||
|
const { inputs, expireAt, usernameTemplate } = data;
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
const connection = await $getClient(providerInputs);
|
const connection = await $getClient(providerInputs);
|
||||||
|
|
||||||
const username = generateUsername();
|
const username = generateUsername(usernameTemplate);
|
||||||
const password = generatePassword();
|
const password = generatePassword();
|
||||||
const expiration = new Date(expireAt).toISOString();
|
const expiration = new Date(expireAt).toISOString();
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,14 @@ const generatePassword = (size = 48) => {
|
|||||||
return customAlphabet(charset, 48)(size);
|
return customAlphabet(charset, 48)(size);
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUsername = () => {
|
const generateUsername = (usernameTemplate?: string | null) => {
|
||||||
return alphaNumericNanoId(25);
|
const randomUsername = `inf_${alphaNumericNanoId(25)}`; // Username must start with an ascii letter, so we prepend the username with "inf-"
|
||||||
|
if (!usernameTemplate) return randomUsername;
|
||||||
|
|
||||||
|
return handlebars.compile(usernameTemplate)({
|
||||||
|
randomUsername,
|
||||||
|
unixTimestamp: Math.floor(Date.now() / 100)
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
enum SapCommands {
|
enum SapCommands {
|
||||||
@@ -81,11 +87,12 @@ export const SapAseProvider = (): TDynamicProviderFns => {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (inputs: unknown) => {
|
const create = async (data: { inputs: unknown; usernameTemplate?: string | null }) => {
|
||||||
|
const { inputs, usernameTemplate } = data;
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
|
|
||||||
const username = `inf_${generateUsername()}`;
|
const username = generateUsername(usernameTemplate);
|
||||||
const password = `${generatePassword()}`;
|
const password = generatePassword();
|
||||||
|
|
||||||
const client = await $getClient(providerInputs);
|
const client = await $getClient(providerInputs);
|
||||||
const masterClient = await $getClient(providerInputs, true);
|
const masterClient = await $getClient(providerInputs, true);
|
||||||
|
|||||||
@@ -21,8 +21,14 @@ const generatePassword = (size = 48) => {
|
|||||||
return customAlphabet(charset, 48)(size);
|
return customAlphabet(charset, 48)(size);
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUsername = () => {
|
const generateUsername = (usernameTemplate?: string | null) => {
|
||||||
return alphaNumericNanoId(32);
|
const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-"
|
||||||
|
if (!usernameTemplate) return randomUsername;
|
||||||
|
|
||||||
|
return handlebars.compile(usernameTemplate)({
|
||||||
|
randomUsername,
|
||||||
|
unixTimestamp: Math.floor(Date.now() / 100)
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SapHanaProvider = (): TDynamicProviderFns => {
|
export const SapHanaProvider = (): TDynamicProviderFns => {
|
||||||
@@ -91,10 +97,11 @@ export const SapHanaProvider = (): TDynamicProviderFns => {
|
|||||||
return testResult;
|
return testResult;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (inputs: unknown, expireAt: number) => {
|
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => {
|
||||||
|
const { inputs, expireAt, usernameTemplate } = data;
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
|
|
||||||
const username = generateUsername();
|
const username = generateUsername(usernameTemplate);
|
||||||
const password = generatePassword();
|
const password = generatePassword();
|
||||||
const expiration = new Date(expireAt).toISOString();
|
const expiration = new Date(expireAt).toISOString();
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,14 @@ const generatePassword = (size = 48) => {
|
|||||||
return customAlphabet(charset, 48)(size);
|
return customAlphabet(charset, 48)(size);
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUsername = () => {
|
const generateUsername = (usernameTemplate?: string | null) => {
|
||||||
return `infisical_${alphaNumericNanoId(32)}`; // username must start with alpha character, hence prefix
|
const randomUsername = `infisical_${alphaNumericNanoId(32)}`; // Username must start with an ascii letter, so we prepend the username with "inf-"
|
||||||
|
if (!usernameTemplate) return randomUsername;
|
||||||
|
|
||||||
|
return handlebars.compile(usernameTemplate)({
|
||||||
|
randomUsername,
|
||||||
|
unixTimestamp: Math.floor(Date.now() / 100)
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDaysToExpiry = (expiryDate: Date) => {
|
const getDaysToExpiry = (expiryDate: Date) => {
|
||||||
@@ -82,12 +88,13 @@ export const SnowflakeProvider = (): TDynamicProviderFns => {
|
|||||||
return isValidConnection;
|
return isValidConnection;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (inputs: unknown, expireAt: number) => {
|
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => {
|
||||||
|
const { inputs, expireAt, usernameTemplate } = data;
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
|
|
||||||
const client = await $getClient(providerInputs);
|
const client = await $getClient(providerInputs);
|
||||||
|
|
||||||
const username = generateUsername();
|
const username = generateUsername(usernameTemplate);
|
||||||
const password = generatePassword();
|
const password = generatePassword();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -104,11 +104,21 @@ const generatePassword = (provider: SqlProviders, requirements?: PasswordRequire
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUsername = (provider: SqlProviders) => {
|
const generateUsername = (provider: SqlProviders, usernameTemplate?: string | null) => {
|
||||||
// For oracle, the client assumes everything is upper case when not using quotes around the password
|
let randomUsername = "";
|
||||||
if (provider === SqlProviders.Oracle) return alphaNumericNanoId(32).toUpperCase();
|
|
||||||
|
|
||||||
return alphaNumericNanoId(32);
|
// For oracle, the client assumes everything is upper case when not using quotes around the password
|
||||||
|
if (provider === SqlProviders.Oracle) {
|
||||||
|
randomUsername = alphaNumericNanoId(32).toUpperCase();
|
||||||
|
} else {
|
||||||
|
randomUsername = alphaNumericNanoId(32);
|
||||||
|
}
|
||||||
|
if (!usernameTemplate) return randomUsername;
|
||||||
|
|
||||||
|
return handlebars.compile(usernameTemplate)({
|
||||||
|
randomUsername,
|
||||||
|
unixTimestamp: Math.floor(Date.now() / 100)
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
type TSqlDatabaseProviderDTO = {
|
type TSqlDatabaseProviderDTO = {
|
||||||
@@ -210,9 +220,12 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
|||||||
return isConnected;
|
return isConnected;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (inputs: unknown, expireAt: number) => {
|
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => {
|
||||||
|
const { inputs, expireAt, usernameTemplate } = data;
|
||||||
|
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
const username = generateUsername(providerInputs.client);
|
const username = generateUsername(providerInputs.client, usernameTemplate);
|
||||||
|
|
||||||
const password = generatePassword(providerInputs.client, providerInputs.passwordRequirements);
|
const password = generatePassword(providerInputs.client, providerInputs.passwordRequirements);
|
||||||
const gatewayCallback = async (host = providerInputs.host, port = providerInputs.port) => {
|
const gatewayCallback = async (host = providerInputs.host, port = providerInputs.port) => {
|
||||||
const db = await $getClient({ ...providerInputs, port, host });
|
const db = await $getClient({ ...providerInputs, port, host });
|
||||||
|
|||||||
@@ -111,9 +111,9 @@ export const groupDALFactory = (db: TDbClient) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (search) {
|
if (search) {
|
||||||
void query.andWhereRaw(`CONCAT_WS(' ', "firstName", "lastName", "username") ilike ?`, [`%${search}%`]);
|
void query.andWhereRaw(`CONCAT_WS(' ', "firstName", "lastName", lower("username")) ilike ?`, [`%${search}%`]);
|
||||||
} else if (username) {
|
} else if (username) {
|
||||||
void query.andWhere(`${TableName.Users}.username`, "ilike", `%${username}%`);
|
void query.andWhereRaw(`lower("${TableName.Users}"."username") ilike ?`, `%${username}%`);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (filter) {
|
switch (filter) {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import {
|
|||||||
import { TUserGroupMembershipDALFactory } from "./user-group-membership-dal";
|
import { TUserGroupMembershipDALFactory } from "./user-group-membership-dal";
|
||||||
|
|
||||||
type TGroupServiceFactoryDep = {
|
type TGroupServiceFactoryDep = {
|
||||||
userDAL: Pick<TUserDALFactory, "find" | "findUserEncKeyByUserIdsBatch" | "transaction" | "findOne">;
|
userDAL: Pick<TUserDALFactory, "find" | "findUserEncKeyByUserIdsBatch" | "transaction" | "findUserByUsername">;
|
||||||
groupDAL: Pick<
|
groupDAL: Pick<
|
||||||
TGroupDALFactory,
|
TGroupDALFactory,
|
||||||
"create" | "findOne" | "update" | "delete" | "findAllGroupPossibleMembers" | "findById" | "transaction"
|
"create" | "findOne" | "update" | "delete" | "findAllGroupPossibleMembers" | "findById" | "transaction"
|
||||||
@@ -380,7 +380,10 @@ export const groupServiceFactory = ({
|
|||||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||||
});
|
});
|
||||||
|
|
||||||
const user = await userDAL.findOne({ username });
|
const usersWithUsername = await userDAL.findUserByUsername(username);
|
||||||
|
// akhilmhdh: case sensitive email resolution
|
||||||
|
const user =
|
||||||
|
usersWithUsername?.length > 1 ? usersWithUsername.find((el) => el.username === username) : usersWithUsername?.[0];
|
||||||
if (!user) throw new NotFoundError({ message: `Failed to find user with username ${username}` });
|
if (!user) throw new NotFoundError({ message: `Failed to find user with username ${username}` });
|
||||||
|
|
||||||
const users = await addUsersToGroupByUserIds({
|
const users = await addUsersToGroupByUserIds({
|
||||||
@@ -461,7 +464,10 @@ export const groupServiceFactory = ({
|
|||||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||||
});
|
});
|
||||||
|
|
||||||
const user = await userDAL.findOne({ username });
|
const usersWithUsername = await userDAL.findUserByUsername(username);
|
||||||
|
// akhilmhdh: case sensitive email resolution
|
||||||
|
const user =
|
||||||
|
usersWithUsername?.length > 1 ? usersWithUsername.find((el) => el.username === username) : usersWithUsername?.[0];
|
||||||
if (!user) throw new NotFoundError({ message: `Failed to find user with username ${username}` });
|
if (!user) throw new NotFoundError({ message: `Failed to find user with username ${username}` });
|
||||||
|
|
||||||
const users = await removeUsersFromGroupByUserIds({
|
const users = await removeUsersFromGroupByUserIds({
|
||||||
|
|||||||
@@ -24,9 +24,13 @@ export const initializeHsmModule = (envConfig: Pick<TEnvConfig, "isHsmConfigured
|
|||||||
isInitialized = true;
|
isInitialized = true;
|
||||||
|
|
||||||
logger.info("PKCS#11 module initialized");
|
logger.info("PKCS#11 module initialized");
|
||||||
} catch (err) {
|
} catch (error) {
|
||||||
logger.error(err, "Failed to initialize PKCS#11 module");
|
if (error instanceof pkcs11js.Pkcs11Error && error.code === pkcs11js.CKR_CRYPTOKI_ALREADY_INITIALIZED) {
|
||||||
throw err;
|
logger.info("Skipping HSM initialization because it's already initialized.");
|
||||||
|
} else {
|
||||||
|
logger.error(error, "Failed to initialize PKCS#11 module");
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -380,7 +380,7 @@ export const ldapConfigServiceFactory = ({
|
|||||||
if (serverCfg.trustLdapEmails) {
|
if (serverCfg.trustLdapEmails) {
|
||||||
newUser = await userDAL.findOne(
|
newUser = await userDAL.findOne(
|
||||||
{
|
{
|
||||||
email,
|
email: email.toLowerCase(),
|
||||||
isEmailVerified: true
|
isEmailVerified: true
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
@@ -391,8 +391,8 @@ export const ldapConfigServiceFactory = ({
|
|||||||
const uniqueUsername = await normalizeUsername(username, userDAL);
|
const uniqueUsername = await normalizeUsername(username, userDAL);
|
||||||
newUser = await userDAL.create(
|
newUser = await userDAL.create(
|
||||||
{
|
{
|
||||||
username: serverCfg.trustLdapEmails ? email : uniqueUsername,
|
username: serverCfg.trustLdapEmails ? email.toLowerCase() : uniqueUsername,
|
||||||
email,
|
email: email.toLowerCase(),
|
||||||
isEmailVerified: serverCfg.trustLdapEmails,
|
isEmailVerified: serverCfg.trustLdapEmails,
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
@@ -429,7 +429,7 @@ export const ldapConfigServiceFactory = ({
|
|||||||
await orgMembershipDAL.create(
|
await orgMembershipDAL.create(
|
||||||
{
|
{
|
||||||
userId: newUser.id,
|
userId: newUser.id,
|
||||||
inviteEmail: email,
|
inviteEmail: email.toLowerCase(),
|
||||||
orgId,
|
orgId,
|
||||||
role,
|
role,
|
||||||
roleId,
|
roleId,
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ export const getDefaultOnPremFeatures = () => {
|
|||||||
secretApproval: true,
|
secretApproval: true,
|
||||||
secretRotation: true,
|
secretRotation: true,
|
||||||
caCrl: false,
|
caCrl: false,
|
||||||
sshHostGroups: false
|
sshHostGroups: false,
|
||||||
|
enterpriseSecretSyncs: false,
|
||||||
|
enterpriseAppConnections: false
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export const licenseDALFactory = (db: TDbClient) => {
|
|||||||
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
||||||
.where(`${TableName.Users}.isGhost`, false)
|
.where(`${TableName.Users}.isGhost`, false)
|
||||||
.count();
|
.count();
|
||||||
return Number(doc?.[0].count);
|
return Number(doc?.[0]?.count ?? 0);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "Count of Org Members" });
|
throw new DatabaseError({ error, name: "Count of Org Members" });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import axios, { AxiosError } from "axios";
|
|||||||
|
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { request } from "@app/lib/config/request";
|
import { request } from "@app/lib/config/request";
|
||||||
|
import { logger } from "@app/lib/logger";
|
||||||
|
|
||||||
import { TFeatureSet } from "./license-types";
|
import { TFeatureSet } from "./license-types";
|
||||||
|
|
||||||
@@ -55,15 +56,24 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
|
|||||||
kmip: false,
|
kmip: false,
|
||||||
gateway: false,
|
gateway: false,
|
||||||
sshHostGroups: false,
|
sshHostGroups: false,
|
||||||
secretScanning: false
|
secretScanning: false,
|
||||||
|
enterpriseSecretSyncs: false,
|
||||||
|
enterpriseAppConnections: false
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setupLicenseRequestWithStore = (baseURL: string, refreshUrl: string, licenseKey: string) => {
|
export const setupLicenseRequestWithStore = (
|
||||||
|
baseURL: string,
|
||||||
|
refreshUrl: string,
|
||||||
|
licenseKey: string,
|
||||||
|
region?: string
|
||||||
|
) => {
|
||||||
let token: string;
|
let token: string;
|
||||||
const licenseReq = axios.create({
|
const licenseReq = axios.create({
|
||||||
baseURL,
|
baseURL,
|
||||||
timeout: 35 * 1000
|
timeout: 35 * 1000,
|
||||||
// signal: AbortSignal.timeout(60 * 1000)
|
headers: {
|
||||||
|
"x-region": region
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const refreshLicense = async () => {
|
const refreshLicense = async () => {
|
||||||
@@ -99,9 +109,10 @@ export const setupLicenseRequestWithStore = (baseURL: string, refreshUrl: string
|
|||||||
(response) => response,
|
(response) => response,
|
||||||
async (err) => {
|
async (err) => {
|
||||||
const originalRequest = (err as AxiosError).config;
|
const originalRequest = (err as AxiosError).config;
|
||||||
|
const errStatusCode = Number((err as AxiosError)?.response?.status);
|
||||||
|
logger.error((err as AxiosError)?.response?.data, "License server call error");
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
if ((err as AxiosError)?.response?.status === 401 && !(originalRequest as any)._retry) {
|
if ((errStatusCode === 401 || errStatusCode === 403) && !(originalRequest as any)._retry) {
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
(originalRequest as any)._retry = true; // injected
|
(originalRequest as any)._retry = true; // injected
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { TIdentityOrgDALFactory } from "@app/services/identity/identity-org-dal"
|
|||||||
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
|
|
||||||
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
import { OrgPermissionBillingActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||||
import { BillingPlanRows, BillingPlanTableHead } from "./licence-enums";
|
import { BillingPlanRows, BillingPlanTableHead } from "./licence-enums";
|
||||||
import { TLicenseDALFactory } from "./license-dal";
|
import { TLicenseDALFactory } from "./license-dal";
|
||||||
@@ -77,13 +77,15 @@ export const licenseServiceFactory = ({
|
|||||||
const licenseServerCloudApi = setupLicenseRequestWithStore(
|
const licenseServerCloudApi = setupLicenseRequestWithStore(
|
||||||
appCfg.LICENSE_SERVER_URL || "",
|
appCfg.LICENSE_SERVER_URL || "",
|
||||||
LICENSE_SERVER_CLOUD_LOGIN,
|
LICENSE_SERVER_CLOUD_LOGIN,
|
||||||
appCfg.LICENSE_SERVER_KEY || ""
|
appCfg.LICENSE_SERVER_KEY || "",
|
||||||
|
appCfg.INTERNAL_REGION
|
||||||
);
|
);
|
||||||
|
|
||||||
const licenseServerOnPremApi = setupLicenseRequestWithStore(
|
const licenseServerOnPremApi = setupLicenseRequestWithStore(
|
||||||
appCfg.LICENSE_SERVER_URL || "",
|
appCfg.LICENSE_SERVER_URL || "",
|
||||||
LICENSE_SERVER_ON_PREM_LOGIN,
|
LICENSE_SERVER_ON_PREM_LOGIN,
|
||||||
appCfg.LICENSE_KEY || ""
|
appCfg.LICENSE_KEY || "",
|
||||||
|
appCfg.INTERNAL_REGION
|
||||||
);
|
);
|
||||||
|
|
||||||
const syncLicenseKeyOnPremFeatures = async (shouldThrow: boolean = false) => {
|
const syncLicenseKeyOnPremFeatures = async (shouldThrow: boolean = false) => {
|
||||||
@@ -92,6 +94,10 @@ export const licenseServiceFactory = ({
|
|||||||
const {
|
const {
|
||||||
data: { currentPlan }
|
data: { currentPlan }
|
||||||
} = await licenseServerOnPremApi.request.get<{ currentPlan: TFeatureSet }>("/api/license/v1/plan");
|
} = await licenseServerOnPremApi.request.get<{ currentPlan: TFeatureSet }>("/api/license/v1/plan");
|
||||||
|
|
||||||
|
const workspacesUsed = await projectDAL.countOfOrgProjects(null);
|
||||||
|
currentPlan.workspacesUsed = workspacesUsed;
|
||||||
|
|
||||||
onPremFeatures = currentPlan;
|
onPremFeatures = currentPlan;
|
||||||
logger.info("Successfully synchronized license key features");
|
logger.info("Successfully synchronized license key features");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -185,6 +191,14 @@ export const licenseServiceFactory = ({
|
|||||||
} = await licenseServerCloudApi.request.get<{ currentPlan: TFeatureSet }>(
|
} = await licenseServerCloudApi.request.get<{ currentPlan: TFeatureSet }>(
|
||||||
`/api/license-server/v1/customers/${org.customerId}/cloud-plan`
|
`/api/license-server/v1/customers/${org.customerId}/cloud-plan`
|
||||||
);
|
);
|
||||||
|
const workspacesUsed = await projectDAL.countOfOrgProjects(orgId);
|
||||||
|
currentPlan.workspacesUsed = workspacesUsed;
|
||||||
|
|
||||||
|
const membersUsed = await licenseDAL.countOfOrgMembers(orgId);
|
||||||
|
currentPlan.membersUsed = membersUsed;
|
||||||
|
const identityUsed = await licenseDAL.countOrgUsersAndIdentities(orgId);
|
||||||
|
currentPlan.identitiesUsed = identityUsed;
|
||||||
|
|
||||||
await keyStore.setItemWithExpiry(
|
await keyStore.setItemWithExpiry(
|
||||||
FEATURE_CACHE_KEY(org.id),
|
FEATURE_CACHE_KEY(org.id),
|
||||||
LICENSE_SERVER_CLOUD_PLAN_TTL,
|
LICENSE_SERVER_CLOUD_PLAN_TTL,
|
||||||
@@ -274,7 +288,7 @@ export const licenseServiceFactory = ({
|
|||||||
billingCycle
|
billingCycle
|
||||||
}: TOrgPlansTableDTO) => {
|
}: TOrgPlansTableDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||||
const { data } = await licenseServerCloudApi.request.get(
|
const { data } = await licenseServerCloudApi.request.get(
|
||||||
`/api/license-server/v1/cloud-products?billing-cycle=${billingCycle}`
|
`/api/license-server/v1/cloud-products?billing-cycle=${billingCycle}`
|
||||||
);
|
);
|
||||||
@@ -296,8 +310,10 @@ export const licenseServiceFactory = ({
|
|||||||
success_url
|
success_url
|
||||||
}: TStartOrgTrialDTO) => {
|
}: TStartOrgTrialDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Billing);
|
OrgPermissionBillingActions.ManageBilling,
|
||||||
|
OrgPermissionSubjects.Billing
|
||||||
|
);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findOrgById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
@@ -324,8 +340,10 @@ export const licenseServiceFactory = ({
|
|||||||
actorOrgId
|
actorOrgId
|
||||||
}: TCreateOrgPortalSession) => {
|
}: TCreateOrgPortalSession) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Billing);
|
OrgPermissionBillingActions.ManageBilling,
|
||||||
|
OrgPermissionSubjects.Billing
|
||||||
|
);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findOrgById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
@@ -348,8 +366,8 @@ export const licenseServiceFactory = ({
|
|||||||
} = await licenseServerCloudApi.request.post(
|
} = await licenseServerCloudApi.request.post(
|
||||||
`/api/license-server/v1/customers/${organization.customerId}/billing-details/payment-methods`,
|
`/api/license-server/v1/customers/${organization.customerId}/billing-details/payment-methods`,
|
||||||
{
|
{
|
||||||
success_url: `${appCfg.SITE_URL}/dashboard`,
|
success_url: `${appCfg.SITE_URL}/organization/billing`,
|
||||||
cancel_url: `${appCfg.SITE_URL}/dashboard`
|
cancel_url: `${appCfg.SITE_URL}/organization/billing`
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -362,7 +380,7 @@ export const licenseServiceFactory = ({
|
|||||||
} = await licenseServerCloudApi.request.post(
|
} = await licenseServerCloudApi.request.post(
|
||||||
`/api/license-server/v1/customers/${organization.customerId}/billing-details/billing-portal`,
|
`/api/license-server/v1/customers/${organization.customerId}/billing-details/billing-portal`,
|
||||||
{
|
{
|
||||||
return_url: `${appCfg.SITE_URL}/dashboard`
|
return_url: `${appCfg.SITE_URL}/organization/billing`
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -371,7 +389,7 @@ export const licenseServiceFactory = ({
|
|||||||
|
|
||||||
const getOrgBillingInfo = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
|
const getOrgBillingInfo = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findOrgById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
@@ -379,7 +397,7 @@ export const licenseServiceFactory = ({
|
|||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (instanceType !== InstanceType.OnPrem && instanceType !== InstanceType.EnterpriseOnPremOffline) {
|
if (instanceType === InstanceType.Cloud) {
|
||||||
const { data } = await licenseServerCloudApi.request.get(
|
const { data } = await licenseServerCloudApi.request.get(
|
||||||
`/api/license-server/v1/customers/${organization.customerId}/cloud-plan/billing`
|
`/api/license-server/v1/customers/${organization.customerId}/cloud-plan/billing`
|
||||||
);
|
);
|
||||||
@@ -399,7 +417,7 @@ export const licenseServiceFactory = ({
|
|||||||
// returns org current plan feature table
|
// returns org current plan feature table
|
||||||
const getOrgPlanTable = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
|
const getOrgPlanTable = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findOrgById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
@@ -407,11 +425,38 @@ export const licenseServiceFactory = ({
|
|||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (instanceType !== InstanceType.OnPrem && instanceType !== InstanceType.EnterpriseOnPremOffline) {
|
|
||||||
const { data } = await licenseServerCloudApi.request.get(
|
const orgMembersUsed = await orgDAL.countAllOrgMembers(orgId);
|
||||||
`/api/license-server/v1/customers/${organization.customerId}/cloud-plan/table`
|
const identityUsed = await identityOrgMembershipDAL.countAllOrgIdentities({ orgId });
|
||||||
);
|
const projects = await projectDAL.find({ orgId });
|
||||||
return data;
|
const projectCount = projects.length;
|
||||||
|
|
||||||
|
if (instanceType === InstanceType.Cloud) {
|
||||||
|
const { data } = await licenseServerCloudApi.request.get<{
|
||||||
|
head: { name: string }[];
|
||||||
|
rows: { name: string; allowed: boolean }[];
|
||||||
|
}>(`/api/license-server/v1/customers/${organization.customerId}/cloud-plan/table`);
|
||||||
|
|
||||||
|
const formattedData = {
|
||||||
|
head: data.head,
|
||||||
|
rows: data.rows.map((el) => {
|
||||||
|
let used = "-";
|
||||||
|
|
||||||
|
if (el.name === BillingPlanRows.MemberLimit.name) {
|
||||||
|
used = orgMembersUsed.toString();
|
||||||
|
} else if (el.name === BillingPlanRows.WorkspaceLimit.name) {
|
||||||
|
used = projectCount.toString();
|
||||||
|
} else if (el.name === BillingPlanRows.IdentityLimit.name) {
|
||||||
|
used = (identityUsed + orgMembersUsed).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...el,
|
||||||
|
used
|
||||||
|
};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
return formattedData;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mappedRows = await Promise.all(
|
const mappedRows = await Promise.all(
|
||||||
@@ -420,14 +465,11 @@ export const licenseServiceFactory = ({
|
|||||||
let used = "-";
|
let used = "-";
|
||||||
|
|
||||||
if (field === BillingPlanRows.MemberLimit.field) {
|
if (field === BillingPlanRows.MemberLimit.field) {
|
||||||
const orgMemberships = await orgDAL.countAllOrgMembers(orgId);
|
used = orgMembersUsed.toString();
|
||||||
used = orgMemberships.toString();
|
|
||||||
} else if (field === BillingPlanRows.WorkspaceLimit.field) {
|
} else if (field === BillingPlanRows.WorkspaceLimit.field) {
|
||||||
const projects = await projectDAL.find({ orgId });
|
used = projectCount.toString();
|
||||||
used = projects.length.toString();
|
|
||||||
} else if (field === BillingPlanRows.IdentityLimit.field) {
|
} else if (field === BillingPlanRows.IdentityLimit.field) {
|
||||||
const identities = await identityOrgMembershipDAL.countAllOrgIdentities({ orgId });
|
used = identityUsed.toString();
|
||||||
used = identities.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -446,7 +488,7 @@ export const licenseServiceFactory = ({
|
|||||||
|
|
||||||
const getOrgBillingDetails = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
|
const getOrgBillingDetails = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findOrgById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
@@ -471,7 +513,10 @@ export const licenseServiceFactory = ({
|
|||||||
email
|
email
|
||||||
}: TUpdateOrgBillingDetailsDTO) => {
|
}: TUpdateOrgBillingDetailsDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
OrgPermissionBillingActions.ManageBilling,
|
||||||
|
OrgPermissionSubjects.Billing
|
||||||
|
);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findOrgById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
@@ -491,7 +536,7 @@ export const licenseServiceFactory = ({
|
|||||||
|
|
||||||
const getOrgPmtMethods = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TOrgPmtMethodsDTO) => {
|
const getOrgPmtMethods = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TOrgPmtMethodsDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findOrgById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
@@ -518,7 +563,10 @@ export const licenseServiceFactory = ({
|
|||||||
cancel_url
|
cancel_url
|
||||||
}: TAddOrgPmtMethodDTO) => {
|
}: TAddOrgPmtMethodDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
OrgPermissionBillingActions.ManageBilling,
|
||||||
|
OrgPermissionSubjects.Billing
|
||||||
|
);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findOrgById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
@@ -547,7 +595,10 @@ export const licenseServiceFactory = ({
|
|||||||
pmtMethodId
|
pmtMethodId
|
||||||
}: TDelOrgPmtMethodDTO) => {
|
}: TDelOrgPmtMethodDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
OrgPermissionBillingActions.ManageBilling,
|
||||||
|
OrgPermissionSubjects.Billing
|
||||||
|
);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findOrgById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
@@ -564,7 +615,7 @@ export const licenseServiceFactory = ({
|
|||||||
|
|
||||||
const getOrgTaxIds = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgTaxIdDTO) => {
|
const getOrgTaxIds = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgTaxIdDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findOrgById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
@@ -582,7 +633,10 @@ export const licenseServiceFactory = ({
|
|||||||
|
|
||||||
const addOrgTaxId = async ({ actorId, actor, actorAuthMethod, actorOrgId, orgId, type, value }: TAddOrgTaxIdDTO) => {
|
const addOrgTaxId = async ({ actorId, actor, actorAuthMethod, actorOrgId, orgId, type, value }: TAddOrgTaxIdDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
OrgPermissionBillingActions.ManageBilling,
|
||||||
|
OrgPermissionSubjects.Billing
|
||||||
|
);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findOrgById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
@@ -603,7 +657,10 @@ export const licenseServiceFactory = ({
|
|||||||
|
|
||||||
const delOrgTaxId = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId, taxId }: TDelOrgTaxIdDTO) => {
|
const delOrgTaxId = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId, taxId }: TDelOrgTaxIdDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
OrgPermissionBillingActions.ManageBilling,
|
||||||
|
OrgPermissionSubjects.Billing
|
||||||
|
);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findOrgById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
@@ -620,7 +677,7 @@ export const licenseServiceFactory = ({
|
|||||||
|
|
||||||
const getOrgTaxInvoices = async ({ actorId, actor, actorOrgId, actorAuthMethod, orgId }: TOrgInvoiceDTO) => {
|
const getOrgTaxInvoices = async ({ actorId, actor, actorOrgId, actorAuthMethod, orgId }: TOrgInvoiceDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findOrgById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
@@ -637,7 +694,7 @@ export const licenseServiceFactory = ({
|
|||||||
|
|
||||||
const getOrgLicenses = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TOrgLicensesDTO) => {
|
const getOrgLicenses = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TOrgLicensesDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findOrgById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export type TFeatureSet = {
|
|||||||
slug: null;
|
slug: null;
|
||||||
tier: -1;
|
tier: -1;
|
||||||
workspaceLimit: null;
|
workspaceLimit: null;
|
||||||
workspacesUsed: 0;
|
workspacesUsed: number;
|
||||||
dynamicSecret: false;
|
dynamicSecret: false;
|
||||||
memberLimit: null;
|
memberLimit: null;
|
||||||
membersUsed: number;
|
membersUsed: number;
|
||||||
@@ -73,6 +73,8 @@ export type TFeatureSet = {
|
|||||||
gateway: false;
|
gateway: false;
|
||||||
sshHostGroups: false;
|
sshHostGroups: false;
|
||||||
secretScanning: false;
|
secretScanning: false;
|
||||||
|
enterpriseSecretSyncs: false;
|
||||||
|
enterpriseAppConnections: false;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TOrgPlansTableDTO = {
|
export type TOrgPlansTableDTO = {
|
||||||
|
|||||||
@@ -171,8 +171,8 @@ export const oidcConfigServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const oidcLogin = async ({
|
const oidcLogin = async ({
|
||||||
externalId,
|
|
||||||
email,
|
email,
|
||||||
|
externalId,
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
orgId,
|
orgId,
|
||||||
@@ -717,7 +717,7 @@ export const oidcConfigServiceFactory = ({
|
|||||||
const groups = typeof claims.groups === "string" ? [claims.groups] : (claims.groups as string[] | undefined);
|
const groups = typeof claims.groups === "string" ? [claims.groups] : (claims.groups as string[] | undefined);
|
||||||
|
|
||||||
oidcLogin({
|
oidcLogin({
|
||||||
email: claims.email,
|
email: claims.email.toLowerCase(),
|
||||||
externalId: claims.sub,
|
externalId: claims.sub,
|
||||||
firstName: claims.given_name ?? "",
|
firstName: claims.given_name ?? "",
|
||||||
lastName: claims.family_name ?? "",
|
lastName: claims.family_name ?? "",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
ProjectPermissionKmipActions,
|
ProjectPermissionKmipActions,
|
||||||
ProjectPermissionMemberActions,
|
ProjectPermissionMemberActions,
|
||||||
ProjectPermissionPkiSubscriberActions,
|
ProjectPermissionPkiSubscriberActions,
|
||||||
|
ProjectPermissionPkiTemplateActions,
|
||||||
ProjectPermissionSecretActions,
|
ProjectPermissionSecretActions,
|
||||||
ProjectPermissionSecretRotationActions,
|
ProjectPermissionSecretRotationActions,
|
||||||
ProjectPermissionSecretScanningConfigActions,
|
ProjectPermissionSecretScanningConfigActions,
|
||||||
@@ -28,7 +29,6 @@ const buildAdminPermissionRules = () => {
|
|||||||
[
|
[
|
||||||
ProjectPermissionSub.SecretFolders,
|
ProjectPermissionSub.SecretFolders,
|
||||||
ProjectPermissionSub.SecretImports,
|
ProjectPermissionSub.SecretImports,
|
||||||
ProjectPermissionSub.SecretApproval,
|
|
||||||
ProjectPermissionSub.Role,
|
ProjectPermissionSub.Role,
|
||||||
ProjectPermissionSub.Integrations,
|
ProjectPermissionSub.Integrations,
|
||||||
ProjectPermissionSub.Webhooks,
|
ProjectPermissionSub.Webhooks,
|
||||||
@@ -39,7 +39,6 @@ const buildAdminPermissionRules = () => {
|
|||||||
ProjectPermissionSub.AuditLogs,
|
ProjectPermissionSub.AuditLogs,
|
||||||
ProjectPermissionSub.IpAllowList,
|
ProjectPermissionSub.IpAllowList,
|
||||||
ProjectPermissionSub.CertificateAuthorities,
|
ProjectPermissionSub.CertificateAuthorities,
|
||||||
ProjectPermissionSub.CertificateTemplates,
|
|
||||||
ProjectPermissionSub.PkiAlerts,
|
ProjectPermissionSub.PkiAlerts,
|
||||||
ProjectPermissionSub.PkiCollections,
|
ProjectPermissionSub.PkiCollections,
|
||||||
ProjectPermissionSub.SshCertificateAuthorities,
|
ProjectPermissionSub.SshCertificateAuthorities,
|
||||||
@@ -58,6 +57,28 @@ const buildAdminPermissionRules = () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionPkiTemplateActions.Read,
|
||||||
|
ProjectPermissionPkiTemplateActions.Edit,
|
||||||
|
ProjectPermissionPkiTemplateActions.Create,
|
||||||
|
ProjectPermissionPkiTemplateActions.Delete,
|
||||||
|
ProjectPermissionPkiTemplateActions.IssueCert,
|
||||||
|
ProjectPermissionPkiTemplateActions.ListCerts
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.CertificateTemplates
|
||||||
|
);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
ProjectPermissionActions.Delete
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.SecretApproval
|
||||||
|
);
|
||||||
|
|
||||||
can(
|
can(
|
||||||
[
|
[
|
||||||
ProjectPermissionCertificateActions.Read,
|
ProjectPermissionCertificateActions.Read,
|
||||||
@@ -365,7 +386,7 @@ const buildMemberPermissionRules = () => {
|
|||||||
ProjectPermissionSub.Certificates
|
ProjectPermissionSub.Certificates
|
||||||
);
|
);
|
||||||
|
|
||||||
can([ProjectPermissionActions.Read], ProjectPermissionSub.CertificateTemplates);
|
can([ProjectPermissionPkiTemplateActions.Read], ProjectPermissionSub.CertificateTemplates);
|
||||||
|
|
||||||
can([ProjectPermissionActions.Read], ProjectPermissionSub.PkiAlerts);
|
can([ProjectPermissionActions.Read], ProjectPermissionSub.PkiAlerts);
|
||||||
can([ProjectPermissionActions.Read], ProjectPermissionSub.PkiCollections);
|
can([ProjectPermissionActions.Read], ProjectPermissionSub.PkiCollections);
|
||||||
@@ -451,6 +472,7 @@ const buildViewerPermissionRules = () => {
|
|||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList);
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.CertificateAuthorities);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.CertificateAuthorities);
|
||||||
can(ProjectPermissionCertificateActions.Read, ProjectPermissionSub.Certificates);
|
can(ProjectPermissionCertificateActions.Read, ProjectPermissionSub.Certificates);
|
||||||
|
can(ProjectPermissionPkiTemplateActions.Read, ProjectPermissionSub.CertificateTemplates);
|
||||||
can(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek);
|
can(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek);
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificates);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificates);
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificateTemplates);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificateTemplates);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user