feat: completed org migration in kms and updated to remove orgDAL functions

This commit is contained in:
=
2025-02-10 15:46:38 +05:30
parent cc28ebd387
commit cbbafcfa42
39 changed files with 1144 additions and 825 deletions
@@ -34,7 +34,7 @@ export async function up(knex: Knex): Promise<void> {
const webhooks = await knex(TableName.Webhook) const webhooks = await knex(TableName.Webhook)
.where({}) .where({})
.leftJoin(TableName.Environment, `${TableName.Environment}.id`, `${TableName.Webhook}.envId`) .join(TableName.Environment, `${TableName.Environment}.id`, `${TableName.Webhook}.envId`)
.select( .select(
"url", "url",
"encryptedSecretKey", "encryptedSecretKey",
@@ -84,7 +84,7 @@ export async function up(knex: Knex): Promise<void> {
: null; : null;
const encryptedUrl = projectKmsService.encryptor({ const encryptedUrl = projectKmsService.encryptor({
plainText: Buffer.from(decryptedUrl || el.url) plainText: Buffer.from(decryptedUrl || el.url || "")
}).cipherTextBlob; }).cipherTextBlob;
return { id: el.id, encryptedUrl, encryptedSecretKey, envId: el.envId }; return { id: el.id, encryptedUrl, encryptedSecretKey, envId: el.envId };
}) })
@@ -30,7 +30,7 @@ export async function up(knex: Knex): Promise<void> {
newRingBuffer<Awaited<ReturnType<(typeof kmsService)["createCipherPairWithDataKey"]>>>(25); newRingBuffer<Awaited<ReturnType<(typeof kmsService)["createCipherPairWithDataKey"]>>>(25);
const secretRotations = await knex(TableName.SecretRotation) const secretRotations = await knex(TableName.SecretRotation)
.leftJoin(TableName.Environment, `${TableName.Environment}.id`, `${TableName.SecretRotation}.envId`) .join(TableName.Environment, `${TableName.Environment}.id`, `${TableName.SecretRotation}.envId`)
.select(selectAllTableCols(TableName.SecretRotation)) .select(selectAllTableCols(TableName.SecretRotation))
.select(knex.ref("projectId").withSchema(TableName.Environment)); .select(knex.ref("projectId").withSchema(TableName.Environment));
@@ -36,8 +36,8 @@ export async function up(knex: Knex): Promise<void> {
newRingBuffer<Awaited<ReturnType<(typeof kmsService)["createCipherPairWithDataKey"]>>>(25); newRingBuffer<Awaited<ReturnType<(typeof kmsService)["createCipherPairWithDataKey"]>>>(25);
const dynamicSecretRootCredentials = await knex(TableName.DynamicSecret) const dynamicSecretRootCredentials = await knex(TableName.DynamicSecret)
.leftJoin(TableName.SecretFolder, `${TableName.SecretFolder}.id`, `${TableName.DynamicSecret}.folderId`) .join(TableName.SecretFolder, `${TableName.SecretFolder}.id`, `${TableName.DynamicSecret}.folderId`)
.leftJoin(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`) .join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`)
.select(selectAllTableCols(TableName.DynamicSecret)) .select(selectAllTableCols(TableName.DynamicSecret))
.select(knex.ref("projectId").withSchema(TableName.Environment)); .select(knex.ref("projectId").withSchema(TableName.Environment));
@@ -0,0 +1,482 @@
import { Knex } from "knex";
import { inMemoryKeyStore } from "@app/keystore/memory";
import { decryptSymmetric, infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
import { selectAllTableCols } from "@app/lib/knex";
import { initLogger } from "@app/lib/logger";
import { KmsDataKey } from "@app/services/kms/kms-types";
import { SecretKeyEncoding, TableName } from "../schemas";
import { getMigrationEnvConfig } from "./utils/env-config";
import { newRingBuffer } from "./utils/ring-buffer";
import { getMigrationEncryptionServices } from "./utils/services";
const BATCH_SIZE = 500;
const reencryptSamlConfig = async (knex: Knex) => {
const hasEncryptedEntrypointColumn = await knex.schema.hasColumn(TableName.SamlConfig, "encryptedSamlEntryPoint");
const hasEncryptedIssuerColumn = await knex.schema.hasColumn(TableName.SamlConfig, "encryptedSamlIssuer");
const hasEncryptedCertificateColumn = await knex.schema.hasColumn(TableName.SamlConfig, "encryptedSamlCertificate");
const hasSamlConfigTable = await knex.schema.hasTable(TableName.SamlConfig);
if (hasSamlConfigTable) {
await knex.schema.alterTable(TableName.SamlConfig, (t) => {
if (!hasEncryptedEntrypointColumn) t.binary("encryptedSamlEntryPoint");
if (!hasEncryptedIssuerColumn) t.binary("encryptedSamlIssuer");
if (!hasEncryptedCertificateColumn) t.binary("encryptedSamlCertificate");
});
}
await initLogger();
const envConfig = getMigrationEnvConfig();
const keyStore = inMemoryKeyStore();
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
const orgEncryptionRingBuffer =
newRingBuffer<Awaited<ReturnType<(typeof kmsService)["createCipherPairWithDataKey"]>>>(25);
const samlConfigs = await knex(TableName.SamlConfig)
.join(TableName.OrgBot, `${TableName.OrgBot}.orgId`, `${TableName.SamlConfig}.orgId`)
.select(selectAllTableCols(TableName.SamlConfig))
.select(
knex.ref("encryptedSymmetricKey").withSchema(TableName.OrgBot),
knex.ref("symmetricKeyIV").withSchema(TableName.OrgBot),
knex.ref("symmetricKeyTag").withSchema(TableName.OrgBot),
knex.ref("symmetricKeyKeyEncoding").withSchema(TableName.OrgBot)
);
const updatedSamlConfigs = await Promise.all(
samlConfigs.map(
async ({ encryptedSymmetricKey, symmetricKeyKeyEncoding, symmetricKeyTag, symmetricKeyIV, ...el }) => {
let orgKmsService = orgEncryptionRingBuffer.getItem(el.orgId);
if (!orgKmsService) {
orgKmsService = await kmsService.createCipherPairWithDataKey({
type: KmsDataKey.Organization,
orgId: el.orgId
});
orgEncryptionRingBuffer.push(el.orgId, orgKmsService);
}
const key = infisicalSymmetricDecrypt({
ciphertext: encryptedSymmetricKey,
iv: symmetricKeyIV,
tag: symmetricKeyTag,
keyEncoding: symmetricKeyKeyEncoding as SecretKeyEncoding
});
const decryptedEntryPoint =
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
el.encryptedEntryPoint && el.entryPointIV && el.entryPointTag
? decryptSymmetric({
key,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
iv: el.entryPointIV,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
tag: el.entryPointTag,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
ciphertext: el.encryptedEntryPoint
})
: "";
const decryptedIssuer =
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
el.encryptedIssuer && el.issuerIV && el.issuerTag
? decryptSymmetric({
key,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
iv: el.issuerIV,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
tag: el.issuerTag,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
ciphertext: el.encryptedIssuer
})
: "";
const decryptedCertificate =
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
el.encryptedCert && el.certIV && el.certTag
? decryptSymmetric({
key,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
iv: el.certIV,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
tag: el.certTag,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
ciphertext: el.encryptedCert
})
: "";
const encryptedSamlIssuer = orgKmsService.encryptor({
plainText: Buffer.from(decryptedIssuer)
}).cipherTextBlob;
const encryptedSamlCertificate = orgKmsService.encryptor({
plainText: Buffer.from(decryptedCertificate)
}).cipherTextBlob;
const encryptedSamlEntryPoint = orgKmsService.encryptor({
plainText: Buffer.from(decryptedEntryPoint)
}).cipherTextBlob;
return { ...el, encryptedSamlCertificate, encryptedSamlEntryPoint, encryptedSamlIssuer };
}
)
);
for (let i = 0; i < updatedSamlConfigs.length; i += BATCH_SIZE) {
// eslint-disable-next-line no-await-in-loop
await knex(TableName.SamlConfig)
.insert(updatedSamlConfigs.slice(i, i + BATCH_SIZE))
.onConflict("id")
.merge();
}
if (hasSamlConfigTable) {
await knex.schema.alterTable(TableName.SamlConfig, (t) => {
if (!hasEncryptedEntrypointColumn) t.binary("encryptedSamlEntryPoint").notNullable().alter();
if (!hasEncryptedIssuerColumn) t.binary("encryptedSamlIssuer").notNullable().alter();
if (!hasEncryptedCertificateColumn) t.binary("encryptedSamlCertificate").notNullable().alter();
});
}
};
const reencryptLdapConfig = async (knex: Knex) => {
const hasEncryptedLdapBindDNColum = await knex.schema.hasColumn(TableName.LdapConfig, "encryptedLdapBindDN");
const hasEncryptedLdapBindPassColumn = await knex.schema.hasColumn(TableName.LdapConfig, "encryptedLdapBindPass");
const hasEncryptedCertificateColumn = await knex.schema.hasColumn(TableName.LdapConfig, "encryptedLdapCaCertificate");
const hasLdapConfigTable = await knex.schema.hasTable(TableName.LdapConfig);
const hasEncryptedCACertColumn = await knex.schema.hasColumn(TableName.LdapConfig, "encryptedCACert");
const hasCaCertIVColumn = await knex.schema.hasColumn(TableName.LdapConfig, "caCertIV");
const hasCaCertTagColumn = await knex.schema.hasColumn(TableName.LdapConfig, "caCertTag");
const hasEncryptedBindPassColumn = await knex.schema.hasColumn(TableName.LdapConfig, "encryptedBindPass");
const hasBindPassIVColumn = await knex.schema.hasColumn(TableName.LdapConfig, "bindPassIV");
const hasBindPassTagColumn = await knex.schema.hasColumn(TableName.LdapConfig, "bindPassTag");
const hasEncryptedBindDNColumn = await knex.schema.hasColumn(TableName.LdapConfig, "encryptedBindDN");
const hasBindDNIVColumn = await knex.schema.hasColumn(TableName.LdapConfig, "bindDNIV");
const hasBindDNTagColumn = await knex.schema.hasColumn(TableName.LdapConfig, "bindDNTag");
if (hasLdapConfigTable) {
await knex.schema.alterTable(TableName.LdapConfig, (t) => {
if (hasEncryptedCACertColumn) t.text("encryptedCACert").nullable().alter();
if (hasCaCertIVColumn) t.string("caCertIV").nullable().alter();
if (hasCaCertTagColumn) t.string("caCertTag").nullable().alter();
if (hasEncryptedBindPassColumn) t.string("encryptedBindPass").nullable().alter();
if (hasBindPassIVColumn) t.string("bindPassIV").nullable().alter();
if (hasBindPassTagColumn) t.string("bindPassTag").nullable().alter();
if (hasEncryptedBindDNColumn) t.string("encryptedBindDN").nullable().alter();
if (hasBindDNIVColumn) t.string("bindDNIV").nullable().alter();
if (hasBindDNTagColumn) t.string("bindDNTag").nullable().alter();
if (!hasEncryptedLdapBindDNColum) t.binary("encryptedLdapBindDN");
if (!hasEncryptedLdapBindPassColumn) t.binary("encryptedLdapBindPass");
if (!hasEncryptedCertificateColumn) t.binary("encryptedLdapCaCertificate");
});
}
await initLogger();
const envConfig = getMigrationEnvConfig();
const keyStore = inMemoryKeyStore();
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
const orgEncryptionRingBuffer =
newRingBuffer<Awaited<ReturnType<(typeof kmsService)["createCipherPairWithDataKey"]>>>(25);
const ldapConfigs = await knex(TableName.LdapConfig)
.join(TableName.OrgBot, `${TableName.OrgBot}.orgId`, `${TableName.LdapConfig}.orgId`)
.select(selectAllTableCols(TableName.LdapConfig))
.select(
knex.ref("encryptedSymmetricKey").withSchema(TableName.OrgBot),
knex.ref("symmetricKeyIV").withSchema(TableName.OrgBot),
knex.ref("symmetricKeyTag").withSchema(TableName.OrgBot),
knex.ref("symmetricKeyKeyEncoding").withSchema(TableName.OrgBot)
);
const updatedLdapConfigs = await Promise.all(
ldapConfigs.map(
async ({ encryptedSymmetricKey, symmetricKeyKeyEncoding, symmetricKeyTag, symmetricKeyIV, ...el }) => {
let orgKmsService = orgEncryptionRingBuffer.getItem(el.orgId);
if (!orgKmsService) {
orgKmsService = await kmsService.createCipherPairWithDataKey({
type: KmsDataKey.Organization,
orgId: el.orgId
});
orgEncryptionRingBuffer.push(el.orgId, orgKmsService);
}
const key = infisicalSymmetricDecrypt({
ciphertext: encryptedSymmetricKey,
iv: symmetricKeyIV,
tag: symmetricKeyTag,
keyEncoding: symmetricKeyKeyEncoding as SecretKeyEncoding
});
const decryptedBindDN =
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
el.encryptedBindDN && el.bindDNIV && el.bindDNTag
? decryptSymmetric({
key,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
iv: el.bindDNIV,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
tag: el.bindDNTag,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
ciphertext: el.encryptedBindDN
})
: "";
const decryptedBindPass =
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
el.encryptedBindPass && el.bindPassIV && el.bindPassTag
? decryptSymmetric({
key,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
iv: el.bindPassIV,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
tag: el.bindPassTag,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
ciphertext: el.encryptedBindPass
})
: "";
const decryptedCertificate =
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
el.encryptedCACert && el.caCertIV && el.caCertTag
? decryptSymmetric({
key,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
iv: el.caCertIV,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
tag: el.caCertTag,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
ciphertext: el.encryptedCACert
})
: "";
const encryptedLdapBindDN = orgKmsService.encryptor({
plainText: Buffer.from(decryptedBindDN)
}).cipherTextBlob;
const encryptedLdapBindPass = orgKmsService.encryptor({
plainText: Buffer.from(decryptedBindPass)
}).cipherTextBlob;
const encryptedLdapCaCertificate = orgKmsService.encryptor({
plainText: Buffer.from(decryptedCertificate)
}).cipherTextBlob;
return { ...el, encryptedLdapBindPass, encryptedLdapBindDN, encryptedLdapCaCertificate };
}
)
);
for (let i = 0; i < updatedLdapConfigs.length; i += BATCH_SIZE) {
// eslint-disable-next-line no-await-in-loop
await knex(TableName.LdapConfig)
.insert(updatedLdapConfigs.slice(i, i + BATCH_SIZE))
.onConflict("id")
.merge();
}
if (hasLdapConfigTable) {
await knex.schema.alterTable(TableName.LdapConfig, (t) => {
if (!hasEncryptedLdapBindPassColumn) t.binary("encryptedLdapBindPass").notNullable().alter();
if (!hasEncryptedLdapBindDNColum) t.binary("encryptedLdapBindDN").notNullable().alter();
if (!hasEncryptedCertificateColumn) t.binary("encryptedLdapCaCertificate").notNullable().alter();
});
}
};
const reencryptOidcConfig = async (knex: Knex) => {
const hasEncryptedOidcClientIdColumn = await knex.schema.hasColumn(TableName.OidcConfig, "encryptedOidcClientId");
const hasEncryptedOidcClientSecretColumn = await knex.schema.hasColumn(
TableName.OidcConfig,
"encryptedOidcClientSecret"
);
const hasEncryptedClientIdColumn = await knex.schema.hasColumn(TableName.OidcConfig, "encryptedClientId");
const hasClientIdIVColumn = await knex.schema.hasColumn(TableName.OidcConfig, "clientIdIV");
const hasClientIdTagColumn = await knex.schema.hasColumn(TableName.OidcConfig, "clientIdTag");
const hasEncryptedClientSecretColumn = await knex.schema.hasColumn(TableName.OidcConfig, "encryptedClientSecret");
const hasClientSecretIVColumn = await knex.schema.hasColumn(TableName.OidcConfig, "clientSecretIV");
const hasClientSecretTagColumn = await knex.schema.hasColumn(TableName.OidcConfig, "clientSecretTag");
const hasOidcConfigTable = await knex.schema.hasTable(TableName.OidcConfig);
if (hasOidcConfigTable) {
await knex.schema.alterTable(TableName.OidcConfig, (t) => {
if (hasEncryptedClientIdColumn) t.text("encryptedClientId").nullable().alter();
if (hasClientIdIVColumn) t.string("clientIdIV").nullable().alter();
if (hasClientIdTagColumn) t.string("clientIdTag").nullable().alter();
if (hasEncryptedClientSecretColumn) t.text("encryptedClientSecret").nullable().alter();
if (hasClientSecretIVColumn) t.string("clientSecretIV").nullable().alter();
if (hasClientSecretTagColumn) t.string("clientSecretTag").nullable().alter();
if (!hasEncryptedOidcClientIdColumn) t.binary("encryptedOidcClientId");
if (!hasEncryptedOidcClientSecretColumn) t.binary("encryptedOidcClientSecret");
});
}
await initLogger();
const envConfig = getMigrationEnvConfig();
const keyStore = inMemoryKeyStore();
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
const orgEncryptionRingBuffer =
newRingBuffer<Awaited<ReturnType<(typeof kmsService)["createCipherPairWithDataKey"]>>>(25);
const oidcConfigs = await knex(TableName.OidcConfig)
.join(TableName.OrgBot, `${TableName.OrgBot}.orgId`, `${TableName.OidcConfig}.orgId`)
.select(selectAllTableCols(TableName.OidcConfig))
.select(
knex.ref("encryptedSymmetricKey").withSchema(TableName.OrgBot),
knex.ref("symmetricKeyIV").withSchema(TableName.OrgBot),
knex.ref("symmetricKeyTag").withSchema(TableName.OrgBot),
knex.ref("symmetricKeyKeyEncoding").withSchema(TableName.OrgBot)
);
const updatedOidcConfigs = await Promise.all(
oidcConfigs.map(
async ({ encryptedSymmetricKey, symmetricKeyKeyEncoding, symmetricKeyTag, symmetricKeyIV, ...el }) => {
let orgKmsService = orgEncryptionRingBuffer.getItem(el.orgId);
if (!orgKmsService) {
orgKmsService = await kmsService.createCipherPairWithDataKey({
type: KmsDataKey.Organization,
orgId: el.orgId
});
orgEncryptionRingBuffer.push(el.orgId, orgKmsService);
}
const key = infisicalSymmetricDecrypt({
ciphertext: encryptedSymmetricKey,
iv: symmetricKeyIV,
tag: symmetricKeyTag,
keyEncoding: symmetricKeyKeyEncoding as SecretKeyEncoding
});
const decryptedClientId =
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
el.encryptedClientId && el.clientIdIV && el.clientIdTag
? decryptSymmetric({
key,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
iv: el.clientIdIV,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
tag: el.clientIdTag,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
ciphertext: el.encryptedClientId
})
: "";
const decryptedClientSecret =
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
el.encryptedClientSecret && el.clientSecretIV && el.clientSecretTag
? decryptSymmetric({
key,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
iv: el.clientSecretIV,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
tag: el.clientSecretTag,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
ciphertext: el.encryptedClientSecret
})
: "";
const encryptedOidcClientId = orgKmsService.encryptor({
plainText: Buffer.from(decryptedClientId)
}).cipherTextBlob;
const encryptedOidcClientSecret = orgKmsService.encryptor({
plainText: Buffer.from(decryptedClientSecret)
}).cipherTextBlob;
return { ...el, encryptedOidcClientId, encryptedOidcClientSecret };
}
)
);
for (let i = 0; i < updatedOidcConfigs.length; i += BATCH_SIZE) {
// eslint-disable-next-line no-await-in-loop
await knex(TableName.OidcConfig)
.insert(updatedOidcConfigs.slice(i, i + BATCH_SIZE))
.onConflict("id")
.merge();
}
if (hasOidcConfigTable) {
await knex.schema.alterTable(TableName.OidcConfig, (t) => {
if (!hasEncryptedOidcClientIdColumn) t.binary("encryptedOidcClientId").notNullable().alter();
if (!hasEncryptedOidcClientSecretColumn) t.binary("encryptedOidcClientSecret").notNullable().alter();
});
}
};
export async function up(knex: Knex): Promise<void> {
await reencryptSamlConfig(knex);
await reencryptLdapConfig(knex);
await reencryptOidcConfig(knex);
}
const dropSamlConfigColumns = async (knex: Knex) => {
const hasEncryptedEntrypointColumn = await knex.schema.hasColumn(TableName.SamlConfig, "encryptedSamlEntryPoint");
const hasEncryptedIssuerColumn = await knex.schema.hasColumn(TableName.SamlConfig, "encryptedSamlIssuer");
const hasEncryptedCertificateColumn = await knex.schema.hasColumn(TableName.SamlConfig, "encryptedSamlCertificate");
const hasSamlConfigTable = await knex.schema.hasTable(TableName.SamlConfig);
if (hasSamlConfigTable) {
await knex.schema.alterTable(TableName.SamlConfig, (t) => {
if (hasEncryptedEntrypointColumn) t.dropColumn("encryptedSamlEntryPoint");
if (hasEncryptedIssuerColumn) t.dropColumn("encryptedSamlIssuer");
if (hasEncryptedCertificateColumn) t.dropColumn("encryptedSamlCertificate");
});
}
};
const dropLdapConfigColumns = async (knex: Knex) => {
const hasEncryptedBindDN = await knex.schema.hasColumn(TableName.LdapConfig, "encryptedLdapBindDN");
const hasEncryptedBindPass = await knex.schema.hasColumn(TableName.LdapConfig, "encryptedLdapBindPass");
const hasEncryptedCertificateColumn = await knex.schema.hasColumn(TableName.LdapConfig, "encryptedLdapCaCertificate");
const hasLdapConfigTable = await knex.schema.hasTable(TableName.LdapConfig);
if (hasLdapConfigTable) {
await knex.schema.alterTable(TableName.LdapConfig, (t) => {
if (hasEncryptedBindDN) t.dropColumn("encryptedLdapBindDN");
if (hasEncryptedBindPass) t.dropColumn("encryptedLdapBindPass");
if (hasEncryptedCertificateColumn) t.dropColumn("encryptedLdapCaCertificate");
});
}
};
const dropOidcConfigColumns = async (knex: Knex) => {
const hasEncryptedClientId = await knex.schema.hasColumn(TableName.OidcConfig, "encryptedOidcClientId");
const hasEncryptedClientSecret = await knex.schema.hasColumn(TableName.OidcConfig, "encryptedOidcClientSecret");
const hasOidcConfigTable = await knex.schema.hasTable(TableName.OidcConfig);
if (hasOidcConfigTable) {
await knex.schema.alterTable(TableName.OidcConfig, (t) => {
if (hasEncryptedClientId) t.dropColumn("encryptedOidcClientId");
if (hasEncryptedClientSecret) t.dropColumn("encryptedOidcClientSecret");
});
}
};
export async function down(knex: Knex): Promise<void> {
await dropSamlConfigColumns(knex);
await dropLdapConfigColumns(knex);
await dropOidcConfigColumns(knex);
}
@@ -0,0 +1,185 @@
import { Knex } from "knex";
import { inMemoryKeyStore } from "@app/keystore/memory";
import { decryptSymmetric, infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
import { selectAllTableCols } from "@app/lib/knex";
import { initLogger } from "@app/lib/logger";
import { KmsDataKey } from "@app/services/kms/kms-types";
import { SecretKeyEncoding, TableName, TOrgBots } from "../schemas";
import { getMigrationEnvConfig } from "./utils/env-config";
import { newRingBuffer } from "./utils/ring-buffer";
import { getMigrationEncryptionServices } from "./utils/services";
const BATCH_SIZE = 500;
const reencryptIdentityK8sAuth = async (knex: Knex) => {
const hasEncryptedKubernetesTokenReviewerJwt = await knex.schema.hasColumn(
TableName.IdentityKubernetesAuth,
"encryptedKubernetesTokenReviewerJwt"
);
const hasEncryptedCertificateColumn = await knex.schema.hasColumn(
TableName.IdentityKubernetesAuth,
"encryptedKubernetesCaCertificate"
);
const hasidentityKubernetesAuthTable = await knex.schema.hasTable(TableName.IdentityKubernetesAuth);
const hasEncryptedCaCertColumn = await knex.schema.hasColumn(TableName.IdentityKubernetesAuth, "encryptedCaCert");
const hasCaCertIVColumn = await knex.schema.hasColumn(TableName.IdentityKubernetesAuth, "caCertIV");
const hasCaCertTagColumn = await knex.schema.hasColumn(TableName.IdentityKubernetesAuth, "caCertTag");
const hasEncryptedTokenReviewerJwtColumn = await knex.schema.hasColumn(
TableName.IdentityKubernetesAuth,
"encryptedTokenReviewerJwt"
);
const hasTokenReviewerJwtIVColumn = await knex.schema.hasColumn(
TableName.IdentityKubernetesAuth,
"tokenReviewerJwtIV"
);
const hasTokenReviewerJwtTagColumn = await knex.schema.hasColumn(
TableName.IdentityKubernetesAuth,
"tokenReviewerJwtTag"
);
if (hasidentityKubernetesAuthTable) {
await knex.schema.alterTable(TableName.IdentityKubernetesAuth, (t) => {
if (hasEncryptedCaCertColumn) t.text("encryptedCaCert").nullable().alter();
if (hasCaCertIVColumn) t.string("caCertIV").nullable().alter();
if (hasCaCertTagColumn) t.string("caCertTag").nullable().alter();
if (hasEncryptedTokenReviewerJwtColumn) t.text("encryptedTokenReviewerJwt").nullable().alter();
if (hasTokenReviewerJwtIVColumn) t.string("tokenReviewerJwtIV").nullable().alter();
if (hasTokenReviewerJwtTagColumn) t.string("tokenReviewerJwtTag").nullable().alter();
if (!hasEncryptedKubernetesTokenReviewerJwt) t.binary("encryptedKubernetesTokenReviewerJwt");
if (!hasEncryptedCertificateColumn) t.binary("encryptedKubernetesCaCertificate");
});
}
await initLogger();
const envConfig = getMigrationEnvConfig();
const keyStore = inMemoryKeyStore();
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
const orgEncryptionRingBuffer =
newRingBuffer<Awaited<ReturnType<(typeof kmsService)["createCipherPairWithDataKey"]>>>(25);
const identityKubernetesConfigs = await knex(TableName.IdentityKubernetesAuth)
.join(
TableName.IdentityOrgMembership,
`${TableName.IdentityOrgMembership}.identityId`,
`${TableName.IdentityKubernetesAuth}.identityId`
)
.join<TOrgBots>(TableName.OrgBot, `${TableName.OrgBot}.orgId`, `${TableName.IdentityOrgMembership}.orgId`)
.select(selectAllTableCols(TableName.IdentityKubernetesAuth))
.select(
knex.ref("encryptedSymmetricKey").withSchema(TableName.OrgBot),
knex.ref("symmetricKeyIV").withSchema(TableName.OrgBot),
knex.ref("symmetricKeyTag").withSchema(TableName.OrgBot),
knex.ref("symmetricKeyKeyEncoding").withSchema(TableName.OrgBot),
knex.ref("orgId").withSchema(TableName.OrgBot)
);
const updatedIdentityKubernetesConfigs = await Promise.all(
identityKubernetesConfigs.map(
async ({ encryptedSymmetricKey, symmetricKeyKeyEncoding, symmetricKeyTag, symmetricKeyIV, orgId, ...el }) => {
let orgKmsService = orgEncryptionRingBuffer.getItem(orgId);
if (!orgKmsService) {
orgKmsService = await kmsService.createCipherPairWithDataKey({
type: KmsDataKey.Organization,
orgId
});
orgEncryptionRingBuffer.push(orgId, orgKmsService);
}
const key = infisicalSymmetricDecrypt({
ciphertext: encryptedSymmetricKey,
iv: symmetricKeyIV,
tag: symmetricKeyTag,
keyEncoding: symmetricKeyKeyEncoding as SecretKeyEncoding
});
const decryptedTokenReviewerJwt =
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
el.encryptedTokenReviewerJwt && el.tokenReviewerJwtIV && el.tokenReviewerJwtTag
? decryptSymmetric({
key,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
iv: el.tokenReviewerJwtIV,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
tag: el.tokenReviewerJwtTag,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
ciphertext: el.encryptedTokenReviewerJwt
})
: "";
const decryptedCertificate =
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
el.encryptedCaCert && el.caCertIV && el.caCertTag
? decryptSymmetric({
key,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
iv: el.caCertIV,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
tag: el.caCertTag,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
ciphertext: el.encryptedCaCert
})
: "";
const encryptedKubernetesTokenReviewerJwt = orgKmsService.encryptor({
plainText: Buffer.from(decryptedTokenReviewerJwt)
}).cipherTextBlob;
const encryptedKubernetesCaCertificate = orgKmsService.encryptor({
plainText: Buffer.from(decryptedCertificate)
}).cipherTextBlob;
return { ...el, encryptedKubernetesCaCertificate, encryptedKubernetesTokenReviewerJwt };
}
)
);
for (let i = 0; i < updatedIdentityKubernetesConfigs.length; i += BATCH_SIZE) {
// eslint-disable-next-line no-await-in-loop
await knex(TableName.IdentityKubernetesAuth)
.insert(updatedIdentityKubernetesConfigs.slice(i, i + BATCH_SIZE))
.onConflict("id")
.merge();
}
if (hasidentityKubernetesAuthTable) {
await knex.schema.alterTable(TableName.IdentityKubernetesAuth, (t) => {
if (!hasEncryptedKubernetesTokenReviewerJwt)
t.binary("encryptedKubernetesTokenReviewerJwt").notNullable().alter();
});
}
};
export async function up(knex: Knex): Promise<void> {
await reencryptIdentityK8sAuth(knex);
}
const dropIdentityK8sColumns = async (knex: Knex) => {
const hasEncryptedKubernetesTokenReviewerJwt = await knex.schema.hasColumn(
TableName.IdentityKubernetesAuth,
"encryptedKubernetesTokenReviewerJwt"
);
const hasEncryptedCertificateColumn = await knex.schema.hasColumn(
TableName.IdentityKubernetesAuth,
"encryptedKubernetesCaCertificate"
);
const hasidentityKubernetesAuthTable = await knex.schema.hasTable(TableName.IdentityKubernetesAuth);
if (hasidentityKubernetesAuthTable) {
await knex.schema.alterTable(TableName.IdentityKubernetesAuth, (t) => {
if (hasEncryptedKubernetesTokenReviewerJwt) t.dropColumn("encryptedKubernetesTokenReviewerJwt");
if (hasEncryptedCertificateColumn) t.dropColumn("encryptedKubernetesCaCertificate");
});
}
};
export async function down(knex: Knex): Promise<void> {
await dropIdentityK8sColumns(knex);
}
@@ -0,0 +1,133 @@
import { Knex } from "knex";
import { inMemoryKeyStore } from "@app/keystore/memory";
import { decryptSymmetric, infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
import { selectAllTableCols } from "@app/lib/knex";
import { initLogger } from "@app/lib/logger";
import { KmsDataKey } from "@app/services/kms/kms-types";
import { SecretKeyEncoding, TableName, TOrgBots } from "../schemas";
import { getMigrationEnvConfig } from "./utils/env-config";
import { newRingBuffer } from "./utils/ring-buffer";
import { getMigrationEncryptionServices } from "./utils/services";
const BATCH_SIZE = 500;
const reencryptIdentityOidcAuth = async (knex: Knex) => {
const hasEncryptedCertificateColumn = await knex.schema.hasColumn(
TableName.IdentityOidcAuth,
"encryptedCaCertificate"
);
const hasidentityOidcAuthTable = await knex.schema.hasTable(TableName.IdentityOidcAuth);
const hasEncryptedCaCertColumn = await knex.schema.hasColumn(TableName.IdentityOidcAuth, "encryptedCaCert");
const hasCaCertIVColumn = await knex.schema.hasColumn(TableName.IdentityOidcAuth, "caCertIV");
const hasCaCertTagColumn = await knex.schema.hasColumn(TableName.IdentityOidcAuth, "caCertTag");
if (hasidentityOidcAuthTable) {
await knex.schema.alterTable(TableName.IdentityOidcAuth, (t) => {
if (hasEncryptedCaCertColumn) t.text("encryptedCaCert").nullable().alter();
if (hasCaCertIVColumn) t.string("caCertIV").nullable().alter();
if (hasCaCertTagColumn) t.string("caCertTag").nullable().alter();
if (!hasEncryptedCertificateColumn) t.binary("encryptedCaCertificate");
});
}
await initLogger();
const envConfig = getMigrationEnvConfig();
const keyStore = inMemoryKeyStore();
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
const orgEncryptionRingBuffer =
newRingBuffer<Awaited<ReturnType<(typeof kmsService)["createCipherPairWithDataKey"]>>>(25);
const identityOidcConfig = await knex(TableName.IdentityOidcAuth)
.join(
TableName.IdentityOrgMembership,
`${TableName.IdentityOrgMembership}.identityId`,
`${TableName.IdentityOidcAuth}.identityId`
)
.join<TOrgBots>(TableName.OrgBot, `${TableName.OrgBot}.orgId`, `${TableName.IdentityOrgMembership}.orgId`)
.select(selectAllTableCols(TableName.IdentityOidcAuth))
.select(
knex.ref("encryptedSymmetricKey").withSchema(TableName.OrgBot),
knex.ref("symmetricKeyIV").withSchema(TableName.OrgBot),
knex.ref("symmetricKeyTag").withSchema(TableName.OrgBot),
knex.ref("symmetricKeyKeyEncoding").withSchema(TableName.OrgBot),
knex.ref("orgId").withSchema(TableName.OrgBot)
);
const updatedIdentityOidcConfigs = await Promise.all(
identityOidcConfig.map(
async ({ encryptedSymmetricKey, symmetricKeyKeyEncoding, symmetricKeyTag, symmetricKeyIV, orgId, ...el }) => {
let orgKmsService = orgEncryptionRingBuffer.getItem(orgId);
if (!orgKmsService) {
orgKmsService = await kmsService.createCipherPairWithDataKey({
type: KmsDataKey.Organization,
orgId
});
orgEncryptionRingBuffer.push(orgId, orgKmsService);
}
const key = infisicalSymmetricDecrypt({
ciphertext: encryptedSymmetricKey,
iv: symmetricKeyIV,
tag: symmetricKeyTag,
keyEncoding: symmetricKeyKeyEncoding as SecretKeyEncoding
});
const decryptedCertificate =
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
el.encryptedCaCert && el.caCertIV && el.caCertTag
? decryptSymmetric({
key,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
iv: el.caCertIV,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
tag: el.caCertTag,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
ciphertext: el.encryptedCaCert
})
: "";
const encryptedCaCertificate = orgKmsService.encryptor({
plainText: Buffer.from(decryptedCertificate)
}).cipherTextBlob;
return { ...el, encryptedCaCertificate };
}
)
);
for (let i = 0; i < updatedIdentityOidcConfigs.length; i += BATCH_SIZE) {
// eslint-disable-next-line no-await-in-loop
await knex(TableName.IdentityOidcAuth)
.insert(updatedIdentityOidcConfigs.slice(i, i + BATCH_SIZE))
.onConflict("id")
.merge();
}
};
export async function up(knex: Knex): Promise<void> {
await reencryptIdentityOidcAuth(knex);
}
const dropIdentityOidcColumns = async (knex: Knex) => {
const hasEncryptedCertificateColumn = await knex.schema.hasColumn(
TableName.IdentityOidcAuth,
"encryptedCaCertificate"
);
const hasidentityOidcTable = await knex.schema.hasTable(TableName.IdentityOidcAuth);
if (hasidentityOidcTable) {
await knex.schema.alterTable(TableName.IdentityOidcAuth, (t) => {
if (hasEncryptedCertificateColumn) t.dropColumn("encryptedCaCertificate");
});
}
};
export async function down(knex: Knex): Promise<void> {
await dropIdentityOidcColumns(knex);
}
+3 -3
View File
@@ -16,9 +16,9 @@ export const DynamicSecretsSchema = z.object({
type: z.string(), type: z.string(),
defaultTTL: z.string(), defaultTTL: z.string(),
maxTTL: z.string().nullable().optional(), maxTTL: z.string().nullable().optional(),
inputIV: z.string(), inputIV: z.string().nullable().optional(),
inputCiphertext: z.string(), inputCiphertext: z.string().nullable().optional(),
inputTag: z.string(), inputTag: z.string().nullable().optional(),
algorithm: z.string().default("aes-256-gcm"), algorithm: z.string().default("aes-256-gcm"),
keyEncoding: z.string().default("utf8"), keyEncoding: z.string().default("utf8"),
folderId: z.string().uuid(), folderId: z.string().uuid(),
@@ -5,6 +5,8 @@
import { z } from "zod"; import { z } from "zod";
import { zodBuffer } from "@app/lib/zod";
import { TImmutableDBKeys } from "./models"; import { TImmutableDBKeys } from "./models";
export const IdentityKubernetesAuthsSchema = z.object({ export const IdentityKubernetesAuthsSchema = z.object({
@@ -17,15 +19,17 @@ export const IdentityKubernetesAuthsSchema = z.object({
updatedAt: z.date(), updatedAt: z.date(),
identityId: z.string().uuid(), identityId: z.string().uuid(),
kubernetesHost: z.string(), kubernetesHost: z.string(),
encryptedCaCert: z.string(), encryptedCaCert: z.string().nullable().optional(),
caCertIV: z.string(), caCertIV: z.string().nullable().optional(),
caCertTag: z.string(), caCertTag: z.string().nullable().optional(),
encryptedTokenReviewerJwt: z.string(), encryptedTokenReviewerJwt: z.string().nullable().optional(),
tokenReviewerJwtIV: z.string(), tokenReviewerJwtIV: z.string().nullable().optional(),
tokenReviewerJwtTag: z.string(), tokenReviewerJwtTag: z.string().nullable().optional(),
allowedNamespaces: z.string(), allowedNamespaces: z.string(),
allowedNames: z.string(), allowedNames: z.string(),
allowedAudience: z.string() allowedAudience: z.string(),
encryptedKubernetesTokenReviewerJwt: zodBuffer,
encryptedKubernetesCaCertificate: zodBuffer.nullable().optional()
}); });
export type TIdentityKubernetesAuths = z.infer<typeof IdentityKubernetesAuthsSchema>; export type TIdentityKubernetesAuths = z.infer<typeof IdentityKubernetesAuthsSchema>;
@@ -5,6 +5,8 @@
import { z } from "zod"; import { z } from "zod";
import { zodBuffer } from "@app/lib/zod";
import { TImmutableDBKeys } from "./models"; import { TImmutableDBKeys } from "./models";
export const IdentityOidcAuthsSchema = z.object({ export const IdentityOidcAuthsSchema = z.object({
@@ -15,15 +17,16 @@ export const IdentityOidcAuthsSchema = z.object({
accessTokenTrustedIps: z.unknown(), accessTokenTrustedIps: z.unknown(),
identityId: z.string().uuid(), identityId: z.string().uuid(),
oidcDiscoveryUrl: z.string(), oidcDiscoveryUrl: z.string(),
encryptedCaCert: z.string(), encryptedCaCert: z.string().nullable().optional(),
caCertIV: z.string(), caCertIV: z.string().nullable().optional(),
caCertTag: z.string(), caCertTag: z.string().nullable().optional(),
boundIssuer: z.string(), boundIssuer: z.string(),
boundAudiences: z.string(), boundAudiences: z.string(),
boundClaims: z.unknown(), boundClaims: z.unknown(),
boundSubject: z.string().nullable().optional(), boundSubject: z.string().nullable().optional(),
createdAt: z.date(), createdAt: z.date(),
updatedAt: z.date() updatedAt: z.date(),
encryptedCaCertificate: zodBuffer.nullable().optional()
}); });
export type TIdentityOidcAuths = z.infer<typeof IdentityOidcAuthsSchema>; export type TIdentityOidcAuths = z.infer<typeof IdentityOidcAuthsSchema>;
+15 -10
View File
@@ -5,6 +5,8 @@
import { z } from "zod"; import { z } from "zod";
import { zodBuffer } from "@app/lib/zod";
import { TImmutableDBKeys } from "./models"; import { TImmutableDBKeys } from "./models";
export const LdapConfigsSchema = z.object({ export const LdapConfigsSchema = z.object({
@@ -12,22 +14,25 @@ export const LdapConfigsSchema = z.object({
orgId: z.string().uuid(), orgId: z.string().uuid(),
isActive: z.boolean(), isActive: z.boolean(),
url: z.string(), url: z.string(),
encryptedBindDN: z.string(), encryptedBindDN: z.string().nullable().optional(),
bindDNIV: z.string(), bindDNIV: z.string().nullable().optional(),
bindDNTag: z.string(), bindDNTag: z.string().nullable().optional(),
encryptedBindPass: z.string(), encryptedBindPass: z.string().nullable().optional(),
bindPassIV: z.string(), bindPassIV: z.string().nullable().optional(),
bindPassTag: z.string(), bindPassTag: z.string().nullable().optional(),
searchBase: z.string(), searchBase: z.string(),
encryptedCACert: z.string(), encryptedCACert: z.string().nullable().optional(),
caCertIV: z.string(), caCertIV: z.string().nullable().optional(),
caCertTag: z.string(), caCertTag: z.string().nullable().optional(),
createdAt: z.date(), createdAt: z.date(),
updatedAt: z.date(), updatedAt: z.date(),
groupSearchBase: z.string().default(""), groupSearchBase: z.string().default(""),
groupSearchFilter: z.string().default(""), groupSearchFilter: z.string().default(""),
searchFilter: z.string().default(""), searchFilter: z.string().default(""),
uniqueUserAttribute: z.string().default("") uniqueUserAttribute: z.string().default(""),
encryptedLdapBindDN: zodBuffer,
encryptedLdapBindPass: zodBuffer,
encryptedLdapCaCertificate: zodBuffer
}); });
export type TLdapConfigs = z.infer<typeof LdapConfigsSchema>; export type TLdapConfigs = z.infer<typeof LdapConfigsSchema>;
+10 -6
View File
@@ -5,6 +5,8 @@
import { z } from "zod"; import { z } from "zod";
import { zodBuffer } from "@app/lib/zod";
import { TImmutableDBKeys } from "./models"; import { TImmutableDBKeys } from "./models";
export const OidcConfigsSchema = z.object({ export const OidcConfigsSchema = z.object({
@@ -15,13 +17,13 @@ export const OidcConfigsSchema = z.object({
jwksUri: z.string().nullable().optional(), jwksUri: z.string().nullable().optional(),
tokenEndpoint: z.string().nullable().optional(), tokenEndpoint: z.string().nullable().optional(),
userinfoEndpoint: z.string().nullable().optional(), userinfoEndpoint: z.string().nullable().optional(),
encryptedClientId: z.string(), encryptedClientId: z.string().nullable().optional(),
configurationType: z.string(), configurationType: z.string(),
clientIdIV: z.string(), clientIdIV: z.string().nullable().optional(),
clientIdTag: z.string(), clientIdTag: z.string().nullable().optional(),
encryptedClientSecret: z.string(), encryptedClientSecret: z.string().nullable().optional(),
clientSecretIV: z.string(), clientSecretIV: z.string().nullable().optional(),
clientSecretTag: z.string(), clientSecretTag: z.string().nullable().optional(),
allowedEmailDomains: z.string().nullable().optional(), allowedEmailDomains: z.string().nullable().optional(),
isActive: z.boolean(), isActive: z.boolean(),
createdAt: z.date(), createdAt: z.date(),
@@ -29,6 +31,8 @@ export const OidcConfigsSchema = z.object({
orgId: z.string().uuid(), orgId: z.string().uuid(),
lastUsed: z.date().nullable().optional(), lastUsed: z.date().nullable().optional(),
manageGroupMemberships: z.boolean().default(false) manageGroupMemberships: z.boolean().default(false)
encryptedOidcClientId: zodBuffer,
encryptedOidcClientSecret: zodBuffer
}); });
export type TOidcConfigs = z.infer<typeof OidcConfigsSchema>; export type TOidcConfigs = z.infer<typeof OidcConfigsSchema>;
+6 -1
View File
@@ -5,6 +5,8 @@
import { z } from "zod"; import { z } from "zod";
import { zodBuffer } from "@app/lib/zod";
import { TImmutableDBKeys } from "./models"; import { TImmutableDBKeys } from "./models";
export const SamlConfigsSchema = z.object({ export const SamlConfigsSchema = z.object({
@@ -23,7 +25,10 @@ export const SamlConfigsSchema = z.object({
createdAt: z.date(), createdAt: z.date(),
updatedAt: z.date(), updatedAt: z.date(),
orgId: z.string().uuid(), orgId: z.string().uuid(),
lastUsed: z.date().nullable().optional() lastUsed: z.date().nullable().optional(),
encryptedSamlEntryPoint: zodBuffer,
encryptedSamlIssuer: zodBuffer,
encryptedSamlCertificate: zodBuffer
}); });
export type TSamlConfigs = z.infer<typeof SamlConfigsSchema>; export type TSamlConfigs = z.infer<typeof SamlConfigsSchema>;
+1 -1
View File
@@ -12,7 +12,7 @@ import { TImmutableDBKeys } from "./models";
export const WebhooksSchema = z.object({ export const WebhooksSchema = z.object({
id: z.string().uuid(), id: z.string().uuid(),
secretPath: z.string().default("/"), secretPath: z.string().default("/"),
url: z.string(), url: z.string().nullable().optional(),
lastStatus: z.string().nullable().optional(), lastStatus: z.string().nullable().optional(),
lastRunErrorMessage: z.string().nullable().optional(), lastRunErrorMessage: z.string().nullable().optional(),
isDisabled: z.boolean().default(false), isDisabled: z.boolean().default(false),
+4 -3
View File
@@ -14,7 +14,7 @@ import { FastifyRequest } from "fastify";
import LdapStrategy from "passport-ldapauth"; import LdapStrategy from "passport-ldapauth";
import { z } from "zod"; import { z } from "zod";
import { LdapConfigsSchema, LdapGroupMapsSchema } from "@app/db/schemas"; import { LdapGroupMapsSchema } from "@app/db/schemas";
import { TLDAPConfig } from "@app/ee/services/ldap-config/ldap-config-types"; import { TLDAPConfig } from "@app/ee/services/ldap-config/ldap-config-types";
import { isValidLdapFilter, searchGroups } from "@app/ee/services/ldap-config/ldap-fns"; import { isValidLdapFilter, searchGroups } from "@app/ee/services/ldap-config/ldap-fns";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
@@ -22,6 +22,7 @@ import { BadRequestError } from "@app/lib/errors";
import { logger } from "@app/lib/logger"; import { logger } from "@app/lib/logger";
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";
import { SanitizedLdapConfigSchema } from "@app/server/routes/sanitizedSchema/directory-config";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
export const registerLdapRouter = async (server: FastifyZodProvider) => { export const registerLdapRouter = async (server: FastifyZodProvider) => {
@@ -187,7 +188,7 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
caCert: z.string().trim().default("") caCert: z.string().trim().default("")
}), }),
response: { response: {
200: LdapConfigsSchema 200: SanitizedLdapConfigSchema
} }
}, },
handler: async (req) => { handler: async (req) => {
@@ -228,7 +229,7 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
.partial() .partial()
.merge(z.object({ organizationId: z.string() })), .merge(z.object({ organizationId: z.string() })),
response: { response: {
200: LdapConfigsSchema 200: SanitizedLdapConfigSchema
} }
}, },
handler: async (req) => { handler: async (req) => {
+19 -17
View File
@@ -11,13 +11,28 @@ import fastifySession from "@fastify/session";
import RedisStore from "connect-redis"; import RedisStore from "connect-redis";
import { z } from "zod"; import { z } from "zod";
import { OidcConfigsSchema } from "@app/db/schemas/oidc-configs"; import { OidcConfigsSchema } from "@app/db/schemas";
import { OIDCConfigurationType } from "@app/ee/services/oidc/oidc-config-types"; import { OIDCConfigurationType } from "@app/ee/services/oidc/oidc-config-types";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { authRateLimit, readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { authRateLimit, 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";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
const SanitizedOidcConfigSchema = OidcConfigsSchema.pick({
id: true,
issuer: true,
authorizationEndpoint: true,
configurationType: true,
discoveryURL: true,
jwksUri: true,
tokenEndpoint: true,
userinfoEndpoint: true,
orgId: true,
isActive: true,
allowedEmailDomains: true,
manageGroupMemberships: true
});
export const registerOidcRouter = async (server: FastifyZodProvider) => { export const registerOidcRouter = async (server: FastifyZodProvider) => {
const appCfg = getConfig(); const appCfg = getConfig();
const passport = new Authenticator({ key: "oidc", userProperty: "passportUser" }); const passport = new Authenticator({ key: "oidc", userProperty: "passportUser" });
@@ -142,7 +157,7 @@ export const registerOidcRouter = async (server: FastifyZodProvider) => {
orgSlug: z.string().trim() orgSlug: z.string().trim()
}), }),
response: { response: {
200: OidcConfigsSchema.pick({ 200: SanitizedOidcConfigSchema.pick({
id: true, id: true,
issuer: true, issuer: true,
authorizationEndpoint: true, authorizationEndpoint: true,
@@ -214,7 +229,7 @@ export const registerOidcRouter = async (server: FastifyZodProvider) => {
.partial() .partial()
.merge(z.object({ orgSlug: z.string() })), .merge(z.object({ orgSlug: z.string() })),
response: { response: {
200: OidcConfigsSchema.pick({ 200: SanitizedOidcConfigSchema.pick({
id: true, id: true,
issuer: true, issuer: true,
authorizationEndpoint: true, authorizationEndpoint: true,
@@ -327,20 +342,7 @@ export const registerOidcRouter = async (server: FastifyZodProvider) => {
} }
}), }),
response: { response: {
200: OidcConfigsSchema.pick({ 200: SanitizedOidcConfigSchema
id: true,
issuer: true,
authorizationEndpoint: true,
configurationType: true,
discoveryURL: true,
jwksUri: true,
tokenEndpoint: true,
userinfoEndpoint: true,
orgId: true,
isActive: true,
allowedEmailDomains: true,
manageGroupMemberships: true
})
} }
}, },
@@ -9,7 +9,7 @@ import { ProjectTemplates } from "@app/lib/api-docs";
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";
import { UnpackedPermissionSchema } from "@app/server/routes/santizedSchemas/permission"; import { UnpackedPermissionSchema } from "@app/server/routes/sanitizedSchema/permission";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
const MAX_JSON_SIZE_LIMIT_IN_BYTES = 32_768; const MAX_JSON_SIZE_LIMIT_IN_BYTES = 32_768;
+3 -3
View File
@@ -12,13 +12,13 @@ import { MultiSamlStrategy } from "@node-saml/passport-saml";
import { FastifyRequest } from "fastify"; import { FastifyRequest } from "fastify";
import { z } from "zod"; import { z } from "zod";
import { SamlConfigsSchema } from "@app/db/schemas";
import { SamlProviders, TGetSamlCfgDTO } from "@app/ee/services/saml-config/saml-config-types"; import { SamlProviders, TGetSamlCfgDTO } from "@app/ee/services/saml-config/saml-config-types";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { logger } from "@app/lib/logger"; import { logger } from "@app/lib/logger";
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";
import { SanitizedSamlConfigSchema } from "@app/server/routes/sanitizedSchema/directory-config";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
type TSAMLConfig = { type TSAMLConfig = {
@@ -298,7 +298,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
cert: z.string() cert: z.string()
}), }),
response: { response: {
200: SamlConfigsSchema 200: SanitizedSamlConfigSchema
} }
}, },
handler: async (req) => { handler: async (req) => {
@@ -333,7 +333,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
.partial() .partial()
.merge(z.object({ organizationId: z.string() })), .merge(z.object({ organizationId: z.string() })),
response: { response: {
200: SamlConfigsSchema 200: SanitizedSamlConfigSchema
} }
}, },
handler: async (req) => { handler: async (req) => {
@@ -9,7 +9,7 @@ import { alphaNumericNanoId } from "@app/lib/nanoid";
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";
import { SanitizedUserProjectAdditionalPrivilegeSchema } from "@app/server/routes/santizedSchemas/user-additional-privilege"; import { SanitizedUserProjectAdditionalPrivilegeSchema } from "@app/server/routes/sanitizedSchema/user-additional-privilege";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => { export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => {
@@ -9,7 +9,7 @@ import { alphaNumericNanoId } from "@app/lib/nanoid";
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";
import { SanitizedIdentityPrivilegeSchema } from "@app/server/routes/santizedSchemas/identitiy-additional-privilege"; import { SanitizedIdentityPrivilegeSchema } from "@app/server/routes/sanitizedSchema/identitiy-additional-privilege";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => { export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => {
@@ -5,7 +5,7 @@ import ms from "ms";
import { ActionProjectType, TableName } from "@app/db/schemas"; import { ActionProjectType, TableName } from "@app/db/schemas";
import { isAtLeastAsPrivileged } from "@app/lib/casl"; import { isAtLeastAsPrivileged } from "@app/lib/casl";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors"; import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { unpackPermissions } from "@app/server/routes/santizedSchemas/permission"; import { unpackPermissions } from "@app/server/routes/sanitizedSchema/permission";
import { ActorType } from "@app/services/auth/auth-type"; import { ActorType } from "@app/services/auth/auth-type";
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal"; import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
import { TProjectDALFactory } from "@app/services/project/project-dal"; import { TProjectDALFactory } from "@app/services/project/project-dal";
@@ -5,7 +5,7 @@ import ms from "ms";
import { ActionProjectType } from "@app/db/schemas"; import { ActionProjectType } from "@app/db/schemas";
import { isAtLeastAsPrivileged } from "@app/lib/casl"; import { isAtLeastAsPrivileged } from "@app/lib/casl";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors"; import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { UnpackedPermissionSchema } from "@app/server/routes/santizedSchemas/permission"; import { UnpackedPermissionSchema } from "@app/server/routes/sanitizedSchema/permission";
import { ActorType } from "@app/services/auth/auth-type"; import { ActorType } from "@app/services/auth/auth-type";
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal"; import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
import { TProjectDALFactory } from "@app/services/project/project-dal"; import { TProjectDALFactory } from "@app/services/project/project-dal";
@@ -1,25 +1,18 @@
import { ForbiddenError } from "@casl/ability"; import { ForbiddenError } from "@casl/ability";
import jwt from "jsonwebtoken"; import jwt from "jsonwebtoken";
import { OrgMembershipStatus, SecretKeyEncoding, TableName, TLdapConfigsUpdate, TUsers } from "@app/db/schemas"; import { OrgMembershipStatus, TableName, TLdapConfigsUpdate, TUsers } from "@app/db/schemas";
import { TGroupDALFactory } from "@app/ee/services/group/group-dal"; import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
import { addUsersToGroupByUserIds, removeUsersFromGroupByUserIds } from "@app/ee/services/group/group-fns"; import { addUsersToGroupByUserIds, removeUsersFromGroupByUserIds } from "@app/ee/services/group/group-fns";
import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal"; import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import {
decryptSymmetric,
encryptSymmetric,
generateAsymmetricKeyPair,
generateSymmetricKey,
infisicalSymmetricDecrypt,
infisicalSymmetricEncypt
} from "@app/lib/crypto/encryption";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors"; import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type"; import { AuthMethod, AuthTokenType } 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 { TokenType } from "@app/services/auth-token/auth-token-types"; import { TokenType } from "@app/services/auth-token/auth-token-types";
import { TGroupProjectDALFactory } from "@app/services/group-project/group-project-dal"; import { TGroupProjectDALFactory } from "@app/services/group-project/group-project-dal";
import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal"; import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { KmsDataKey } from "@app/services/kms/kms-types";
import { TOrgDALFactory } from "@app/services/org/org-dal"; import { TOrgDALFactory } from "@app/services/org/org-dal";
import { getDefaultOrgMembershipRole } from "@app/services/org/org-role-fns"; import { getDefaultOrgMembershipRole } from "@app/services/org/org-role-fns";
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal"; import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
@@ -59,7 +52,6 @@ type TLdapConfigServiceFactoryDep = {
TOrgDALFactory, TOrgDALFactory,
"createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById" "createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById"
>; >;
orgBotDAL: Pick<TOrgBotDALFactory, "findOne" | "create" | "transaction">;
groupDAL: Pick<TGroupDALFactory, "find" | "findOne">; groupDAL: Pick<TGroupDALFactory, "find" | "findOne">;
groupProjectDAL: Pick<TGroupProjectDALFactory, "find">; groupProjectDAL: Pick<TGroupProjectDALFactory, "find">;
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "findLatestProjectKey" | "insertMany" | "delete">; projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "findLatestProjectKey" | "insertMany" | "delete">;
@@ -84,6 +76,7 @@ type TLdapConfigServiceFactoryDep = {
licenseService: Pick<TLicenseServiceFactory, "getPlan" | "updateSubscriptionOrgMemberCount">; licenseService: Pick<TLicenseServiceFactory, "getPlan" | "updateSubscriptionOrgMemberCount">;
tokenService: Pick<TAuthTokenServiceFactory, "createTokenForUser">; tokenService: Pick<TAuthTokenServiceFactory, "createTokenForUser">;
smtpService: Pick<TSmtpService, "sendMail">; smtpService: Pick<TSmtpService, "sendMail">;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
}; };
export type TLdapConfigServiceFactory = ReturnType<typeof ldapConfigServiceFactory>; export type TLdapConfigServiceFactory = ReturnType<typeof ldapConfigServiceFactory>;
@@ -93,7 +86,6 @@ export const ldapConfigServiceFactory = ({
ldapGroupMapDAL, ldapGroupMapDAL,
orgDAL, orgDAL,
orgMembershipDAL, orgMembershipDAL,
orgBotDAL,
groupDAL, groupDAL,
groupProjectDAL, groupProjectDAL,
projectKeyDAL, projectKeyDAL,
@@ -105,7 +97,8 @@ export const ldapConfigServiceFactory = ({
permissionService, permissionService,
licenseService, licenseService,
tokenService, tokenService,
smtpService smtpService,
kmsService
}: TLdapConfigServiceFactoryDep) => { }: TLdapConfigServiceFactoryDep) => {
const createLdapCfg = async ({ const createLdapCfg = async ({
actor, actor,
@@ -133,77 +126,23 @@ export const ldapConfigServiceFactory = ({
message: message:
"Failed to create LDAP configuration due to plan restriction. Upgrade plan to create LDAP configuration." "Failed to create LDAP configuration due to plan restriction. Upgrade plan to create LDAP configuration."
}); });
const { encryptor } = await kmsService.createCipherPairWithDataKey({
const orgBot = await orgBotDAL.transaction(async (tx) => { type: KmsDataKey.Organization,
const doc = await orgBotDAL.findOne({ orgId }, tx); orgId
if (doc) return doc;
const { privateKey, publicKey } = generateAsymmetricKeyPair();
const key = generateSymmetricKey();
const {
ciphertext: encryptedPrivateKey,
iv: privateKeyIV,
tag: privateKeyTag,
encoding: privateKeyKeyEncoding,
algorithm: privateKeyAlgorithm
} = infisicalSymmetricEncypt(privateKey);
const {
ciphertext: encryptedSymmetricKey,
iv: symmetricKeyIV,
tag: symmetricKeyTag,
encoding: symmetricKeyKeyEncoding,
algorithm: symmetricKeyAlgorithm
} = infisicalSymmetricEncypt(key);
return orgBotDAL.create(
{
name: "Infisical org bot",
publicKey,
privateKeyIV,
encryptedPrivateKey,
symmetricKeyIV,
symmetricKeyTag,
encryptedSymmetricKey,
symmetricKeyAlgorithm,
orgId,
privateKeyTag,
privateKeyAlgorithm,
privateKeyKeyEncoding,
symmetricKeyKeyEncoding
},
tx
);
}); });
const key = infisicalSymmetricDecrypt({
ciphertext: orgBot.encryptedSymmetricKey,
iv: orgBot.symmetricKeyIV,
tag: orgBot.symmetricKeyTag,
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
});
const { ciphertext: encryptedBindDN, iv: bindDNIV, tag: bindDNTag } = encryptSymmetric(bindDN, key);
const { ciphertext: encryptedBindPass, iv: bindPassIV, tag: bindPassTag } = encryptSymmetric(bindPass, key);
const { ciphertext: encryptedCACert, iv: caCertIV, tag: caCertTag } = encryptSymmetric(caCert, key);
const ldapConfig = await ldapConfigDAL.create({ const ldapConfig = await ldapConfigDAL.create({
orgId, orgId,
isActive, isActive,
url, url,
encryptedBindDN,
bindDNIV,
bindDNTag,
encryptedBindPass,
bindPassIV,
bindPassTag,
uniqueUserAttribute, uniqueUserAttribute,
searchBase, searchBase,
searchFilter, searchFilter,
groupSearchBase, groupSearchBase,
groupSearchFilter, groupSearchFilter,
encryptedCACert, encryptedLdapCaCertificate: encryptor({ plainText: Buffer.from(caCert) }).cipherTextBlob,
caCertIV, encryptedLdapBindDN: encryptor({ plainText: Buffer.from(bindDN) }).cipherTextBlob,
caCertTag encryptedLdapBindPass: encryptor({ plainText: Buffer.from(bindPass) }).cipherTextBlob
}); });
return ldapConfig; return ldapConfig;
@@ -246,38 +185,21 @@ export const ldapConfigServiceFactory = ({
uniqueUserAttribute uniqueUserAttribute
}; };
const orgBot = await orgBotDAL.findOne({ orgId }); const { encryptor } = await kmsService.createCipherPairWithDataKey({
if (!orgBot) type: KmsDataKey.Organization,
throw new NotFoundError({ orgId
message: `Organization bot in organization with ID '${orgId}' not found`,
name: "OrgBotNotFound"
});
const key = infisicalSymmetricDecrypt({
ciphertext: orgBot.encryptedSymmetricKey,
iv: orgBot.symmetricKeyIV,
tag: orgBot.symmetricKeyTag,
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
}); });
if (bindDN !== undefined) { if (bindDN !== undefined) {
const { ciphertext: encryptedBindDN, iv: bindDNIV, tag: bindDNTag } = encryptSymmetric(bindDN, key); updateQuery.encryptedLdapBindDN = encryptor({ plainText: Buffer.from(bindDN) }).cipherTextBlob;
updateQuery.encryptedBindDN = encryptedBindDN;
updateQuery.bindDNIV = bindDNIV;
updateQuery.bindDNTag = bindDNTag;
} }
if (bindPass !== undefined) { if (bindPass !== undefined) {
const { ciphertext: encryptedBindPass, iv: bindPassIV, tag: bindPassTag } = encryptSymmetric(bindPass, key); updateQuery.encryptedLdapBindPass = encryptor({ plainText: Buffer.from(bindPass) }).cipherTextBlob;
updateQuery.encryptedBindPass = encryptedBindPass;
updateQuery.bindPassIV = bindPassIV;
updateQuery.bindPassTag = bindPassTag;
} }
if (caCert !== undefined) { if (caCert !== undefined) {
const { ciphertext: encryptedCACert, iv: caCertIV, tag: caCertTag } = encryptSymmetric(caCert, key); updateQuery.encryptedLdapCaCertificate = encryptor({ plainText: Buffer.from(caCert) }).cipherTextBlob;
updateQuery.encryptedCACert = encryptedCACert;
updateQuery.caCertIV = caCertIV;
updateQuery.caCertTag = caCertTag;
} }
const [ldapConfig] = await ldapConfigDAL.update({ orgId }, updateQuery); const [ldapConfig] = await ldapConfigDAL.update({ orgId }, updateQuery);
@@ -293,61 +215,24 @@ export const ldapConfigServiceFactory = ({
}); });
} }
const orgBot = await orgBotDAL.findOne({ orgId: ldapConfig.orgId }); const { decryptor } = await kmsService.createCipherPairWithDataKey({
if (!orgBot) { type: KmsDataKey.Organization,
throw new NotFoundError({ orgId: ldapConfig.orgId
message: `Organization bot not found in organization with ID ${ldapConfig.orgId}`,
name: "OrgBotNotFound"
});
}
const key = infisicalSymmetricDecrypt({
ciphertext: orgBot.encryptedSymmetricKey,
iv: orgBot.symmetricKeyIV,
tag: orgBot.symmetricKeyTag,
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
}); });
const {
encryptedBindDN,
bindDNIV,
bindDNTag,
encryptedBindPass,
bindPassIV,
bindPassTag,
encryptedCACert,
caCertIV,
caCertTag
} = ldapConfig;
let bindDN = ""; let bindDN = "";
if (encryptedBindDN && bindDNIV && bindDNTag) { if (ldapConfig.encryptedLdapBindDN) {
bindDN = decryptSymmetric({ bindDN = decryptor({ cipherTextBlob: ldapConfig.encryptedLdapBindDN }).toString();
ciphertext: encryptedBindDN,
key,
tag: bindDNTag,
iv: bindDNIV
});
} }
let bindPass = ""; let bindPass = "";
if (encryptedBindPass && bindPassIV && bindPassTag) { if (ldapConfig.encryptedLdapBindPass) {
bindPass = decryptSymmetric({ bindPass = decryptor({ cipherTextBlob: ldapConfig.encryptedLdapBindPass }).toString();
ciphertext: encryptedBindPass,
key,
tag: bindPassTag,
iv: bindPassIV
});
} }
let caCert = ""; let caCert = "";
if (encryptedCACert && caCertIV && caCertTag) { if (ldapConfig.encryptedLdapCaCertificate) {
caCert = decryptSymmetric({ caCert = decryptor({ cipherTextBlob: ldapConfig.encryptedLdapCaCertificate }).toString();
ciphertext: encryptedCACert,
key,
tag: caCertTag,
iv: caCertIV
});
} }
return { return {
@@ -3,7 +3,7 @@ import { ForbiddenError } from "@casl/ability";
import jwt from "jsonwebtoken"; import jwt from "jsonwebtoken";
import { Issuer, Issuer as OpenIdIssuer, Strategy as OpenIdStrategy, TokenSet } from "openid-client"; import { Issuer, Issuer as OpenIdIssuer, Strategy as OpenIdStrategy, TokenSet } from "openid-client";
import { OrgMembershipStatus, SecretKeyEncoding, TableName, TUsers } from "@app/db/schemas"; import { OrgMembershipStatus, TableName, TUsers } from "@app/db/schemas";
import { TOidcConfigsUpdate } from "@app/db/schemas/oidc-configs"; import { TOidcConfigsUpdate } from "@app/db/schemas/oidc-configs";
import { TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-service"; import { TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-service";
import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { EventType } from "@app/ee/services/audit-log/audit-log-types";
@@ -14,21 +14,14 @@ import { TLicenseServiceFactory } from "@app/ee/services/license/license-service
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import {
decryptSymmetric,
encryptSymmetric,
generateAsymmetricKeyPair,
generateSymmetricKey,
infisicalSymmetricDecrypt,
infisicalSymmetricEncypt
} from "@app/lib/crypto/encryption";
import { BadRequestError, ForbiddenRequestError, NotFoundError, OidcAuthError } from "@app/lib/errors"; import { BadRequestError, ForbiddenRequestError, NotFoundError, OidcAuthError } from "@app/lib/errors";
import { OrgServiceActor } from "@app/lib/types"; import { OrgServiceActor } from "@app/lib/types";
import { ActorType, AuthMethod, AuthTokenType } from "@app/services/auth/auth-type"; import { ActorType, AuthMethod, AuthTokenType } 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 { TokenType } from "@app/services/auth-token/auth-token-types"; import { TokenType } from "@app/services/auth-token/auth-token-types";
import { TGroupProjectDALFactory } from "@app/services/group-project/group-project-dal"; import { TGroupProjectDALFactory } from "@app/services/group-project/group-project-dal";
import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal"; import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { KmsDataKey } from "@app/services/kms/kms-types";
import { TOrgDALFactory } from "@app/services/org/org-dal"; import { TOrgDALFactory } from "@app/services/org/org-dal";
import { getDefaultOrgMembershipRole } from "@app/services/org/org-role-fns"; import { getDefaultOrgMembershipRole } from "@app/services/org/org-role-fns";
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal"; import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
@@ -70,7 +63,6 @@ type TOidcConfigServiceFactoryDep = {
"createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById" "createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById"
>; >;
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "create">; orgMembershipDAL: Pick<TOrgMembershipDALFactory, "create">;
orgBotDAL: Pick<TOrgBotDALFactory, "findOne" | "create" | "transaction">;
licenseService: Pick<TLicenseServiceFactory, "getPlan" | "updateSubscriptionOrgMemberCount">; licenseService: Pick<TLicenseServiceFactory, "getPlan" | "updateSubscriptionOrgMemberCount">;
tokenService: Pick<TAuthTokenServiceFactory, "createTokenForUser">; tokenService: Pick<TAuthTokenServiceFactory, "createTokenForUser">;
smtpService: Pick<TSmtpService, "sendMail" | "verify">; smtpService: Pick<TSmtpService, "sendMail" | "verify">;
@@ -91,6 +83,7 @@ type TOidcConfigServiceFactoryDep = {
projectDAL: Pick<TProjectDALFactory, "findProjectGhostUser">; projectDAL: Pick<TProjectDALFactory, "findProjectGhostUser">;
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">; projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
auditLogService: Pick<TAuditLogServiceFactory, "createAuditLog">; auditLogService: Pick<TAuditLogServiceFactory, "createAuditLog">;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
}; };
export type TOidcConfigServiceFactory = ReturnType<typeof oidcConfigServiceFactory>; export type TOidcConfigServiceFactory = ReturnType<typeof oidcConfigServiceFactory>;
@@ -103,7 +96,6 @@ export const oidcConfigServiceFactory = ({
licenseService, licenseService,
permissionService, permissionService,
tokenService, tokenService,
orgBotDAL,
smtpService, smtpService,
oidcConfigDAL, oidcConfigDAL,
userGroupMembershipDAL, userGroupMembershipDAL,
@@ -112,7 +104,8 @@ export const oidcConfigServiceFactory = ({
projectKeyDAL, projectKeyDAL,
projectDAL, projectDAL,
projectBotDAL, projectBotDAL,
auditLogService auditLogService,
kmsService
}: TOidcConfigServiceFactoryDep) => { }: TOidcConfigServiceFactoryDep) => {
const getOidc = async (dto: TGetOidcCfgDTO) => { const getOidc = async (dto: TGetOidcCfgDTO) => {
const org = await orgDAL.findOne({ slug: dto.orgSlug }); const org = await orgDAL.findOne({ slug: dto.orgSlug });
@@ -143,43 +136,19 @@ export const oidcConfigServiceFactory = ({
}); });
} }
// decrypt and return cfg const { decryptor } = await kmsService.createCipherPairWithDataKey({
const orgBot = await orgBotDAL.findOne({ orgId: oidcCfg.orgId }); type: KmsDataKey.Organization,
if (!orgBot) { orgId: oidcCfg.orgId
throw new NotFoundError({
message: `Organization bot for organization with ID '${oidcCfg.orgId}' not found`,
name: "OrgBotNotFound"
});
}
const key = infisicalSymmetricDecrypt({
ciphertext: orgBot.encryptedSymmetricKey,
iv: orgBot.symmetricKeyIV,
tag: orgBot.symmetricKeyTag,
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
}); });
const { encryptedClientId, clientIdIV, clientIdTag, encryptedClientSecret, clientSecretIV, clientSecretTag } =
oidcCfg;
let clientId = ""; let clientId = "";
if (encryptedClientId && clientIdIV && clientIdTag) { if (oidcCfg.encryptedOidcClientId) {
clientId = decryptSymmetric({ clientId = decryptor({ cipherTextBlob: oidcCfg.encryptedOidcClientId }).toString();
ciphertext: encryptedClientId,
key,
tag: clientIdTag,
iv: clientIdIV
});
} }
let clientSecret = ""; let clientSecret = "";
if (encryptedClientSecret && clientSecretIV && clientSecretTag) { if (oidcCfg.encryptedOidcClientSecret) {
clientSecret = decryptSymmetric({ clientSecret = decryptor({ cipherTextBlob: oidcCfg.encryptedOidcClientSecret }).toString();
key,
tag: clientSecretTag,
iv: clientSecretIV,
ciphertext: encryptedClientSecret
});
} }
return { return {
@@ -540,12 +509,10 @@ export const oidcConfigServiceFactory = ({
); );
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Sso); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Sso);
const orgBot = await orgBotDAL.findOne({ orgId: org.id }); const { encryptor } = await kmsService.createCipherPairWithDataKey({
if (!orgBot) type: KmsDataKey.Organization,
throw new NotFoundError({ orgId: org.id
message: `Organization bot for organization with ID '${org.id}' not found`, });
name: "OrgBotNotFound"
});
const serverCfg = await getServerCfg(); const serverCfg = await getServerCfg();
if (isActive && !serverCfg.trustOidcEmails) { if (isActive && !serverCfg.trustOidcEmails) {
@@ -558,13 +525,6 @@ export const oidcConfigServiceFactory = ({
} }
} }
const key = infisicalSymmetricDecrypt({
ciphertext: orgBot.encryptedSymmetricKey,
iv: orgBot.symmetricKeyIV,
tag: orgBot.symmetricKeyTag,
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
});
const updateQuery: TOidcConfigsUpdate = { const updateQuery: TOidcConfigsUpdate = {
allowedEmailDomains, allowedEmailDomains,
configurationType, configurationType,
@@ -580,22 +540,11 @@ export const oidcConfigServiceFactory = ({
}; };
if (clientId !== undefined) { if (clientId !== undefined) {
const { ciphertext: encryptedClientId, iv: clientIdIV, tag: clientIdTag } = encryptSymmetric(clientId, key); updateQuery.encryptedOidcClientId = encryptor({ plainText: Buffer.from(clientId) }).cipherTextBlob;
updateQuery.encryptedClientId = encryptedClientId;
updateQuery.clientIdIV = clientIdIV;
updateQuery.clientIdTag = clientIdTag;
} }
if (clientSecret !== undefined) { if (clientSecret !== undefined) {
const { updateQuery.encryptedOidcClientSecret = encryptor({ plainText: Buffer.from(clientSecret) }).cipherTextBlob;
ciphertext: encryptedClientSecret,
iv: clientSecretIV,
tag: clientSecretTag
} = encryptSymmetric(clientSecret, key);
updateQuery.encryptedClientSecret = encryptedClientSecret;
updateQuery.clientSecretIV = clientSecretIV;
updateQuery.clientSecretTag = clientSecretTag;
} }
const [ssoConfig] = await oidcConfigDAL.update({ orgId: org.id }, updateQuery); const [ssoConfig] = await oidcConfigDAL.update({ orgId: org.id }, updateQuery);
@@ -647,61 +596,11 @@ export const oidcConfigServiceFactory = ({
); );
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Sso); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Sso);
const orgBot = await orgBotDAL.transaction(async (tx) => { const { encryptor } = await kmsService.createCipherPairWithDataKey({
const doc = await orgBotDAL.findOne({ orgId: org.id }, tx); type: KmsDataKey.Organization,
if (doc) return doc; orgId: org.id
const { privateKey, publicKey } = generateAsymmetricKeyPair();
const key = generateSymmetricKey();
const {
ciphertext: encryptedPrivateKey,
iv: privateKeyIV,
tag: privateKeyTag,
encoding: privateKeyKeyEncoding,
algorithm: privateKeyAlgorithm
} = infisicalSymmetricEncypt(privateKey);
const {
ciphertext: encryptedSymmetricKey,
iv: symmetricKeyIV,
tag: symmetricKeyTag,
encoding: symmetricKeyKeyEncoding,
algorithm: symmetricKeyAlgorithm
} = infisicalSymmetricEncypt(key);
return orgBotDAL.create(
{
name: "Infisical org bot",
publicKey,
privateKeyIV,
encryptedPrivateKey,
symmetricKeyIV,
symmetricKeyTag,
encryptedSymmetricKey,
symmetricKeyAlgorithm,
orgId: org.id,
privateKeyTag,
privateKeyAlgorithm,
privateKeyKeyEncoding,
symmetricKeyKeyEncoding
},
tx
);
}); });
const key = infisicalSymmetricDecrypt({
ciphertext: orgBot.encryptedSymmetricKey,
iv: orgBot.symmetricKeyIV,
tag: orgBot.symmetricKeyTag,
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
});
const { ciphertext: encryptedClientId, iv: clientIdIV, tag: clientIdTag } = encryptSymmetric(clientId, key);
const {
ciphertext: encryptedClientSecret,
iv: clientSecretIV,
tag: clientSecretTag
} = encryptSymmetric(clientSecret, key);
const oidcCfg = await oidcConfigDAL.create({ const oidcCfg = await oidcConfigDAL.create({
issuer, issuer,
isActive, isActive,
@@ -713,13 +612,9 @@ export const oidcConfigServiceFactory = ({
tokenEndpoint, tokenEndpoint,
userinfoEndpoint, userinfoEndpoint,
orgId: org.id, orgId: org.id,
encryptedClientId,
clientIdIV,
clientIdTag,
encryptedClientSecret,
clientSecretIV,
clientSecretTag,
manageGroupMemberships manageGroupMemberships
encryptedOidcClientId: encryptor({ plainText: Buffer.from(clientId) }).cipherTextBlob,
encryptedOidcClientSecret: encryptor({ plainText: Buffer.from(clientSecret) }).cipherTextBlob
}); });
return oidcCfg; return oidcCfg;
@@ -6,7 +6,7 @@ import {
CASL_ACTION_SCHEMA_NATIVE_ENUM CASL_ACTION_SCHEMA_NATIVE_ENUM
} from "@app/ee/services/permission/permission-schemas"; } from "@app/ee/services/permission/permission-schemas";
import { conditionsMatcher, PermissionConditionOperators } from "@app/lib/casl"; import { conditionsMatcher, PermissionConditionOperators } from "@app/lib/casl";
import { UnpackedPermissionSchema } from "@app/server/routes/santizedSchemas/permission"; import { UnpackedPermissionSchema } from "@app/server/routes/sanitizedSchema/permission";
import { PermissionConditionSchema } from "./permission-types"; import { PermissionConditionSchema } from "./permission-types";
@@ -15,7 +15,7 @@ import {
} from "@app/ee/services/project-template/project-template-types"; } from "@app/ee/services/project-template/project-template-types";
import { BadRequestError, NotFoundError } from "@app/lib/errors"; import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { OrgServiceActor } from "@app/lib/types"; import { OrgServiceActor } from "@app/lib/types";
import { unpackPermissions } from "@app/server/routes/santizedSchemas/permission"; import { unpackPermissions } from "@app/server/routes/sanitizedSchema/permission";
import { getPredefinedRoles } from "@app/services/project-role/project-role-fns"; import { getPredefinedRoles } from "@app/services/project-role/project-role-fns";
import { TProjectTemplateDALFactory } from "./project-template-dal"; import { TProjectTemplateDALFactory } from "./project-template-dal";
@@ -2,7 +2,7 @@ import { z } from "zod";
import { TProjectEnvironments } from "@app/db/schemas"; import { TProjectEnvironments } from "@app/db/schemas";
import { TProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission"; import { TProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
import { UnpackedPermissionSchema } from "@app/server/routes/santizedSchemas/permission"; import { UnpackedPermissionSchema } from "@app/server/routes/sanitizedSchema/permission";
export type TProjectTemplateEnvironment = Pick<TProjectEnvironments, "name" | "slug" | "position">; export type TProjectTemplateEnvironment = Pick<TProjectEnvironments, "name" | "slug" | "position">;
@@ -5,7 +5,7 @@ import ms from "ms";
import { ActionProjectType, TableName } from "@app/db/schemas"; import { ActionProjectType, TableName } from "@app/db/schemas";
import { isAtLeastAsPrivileged } from "@app/lib/casl"; import { isAtLeastAsPrivileged } from "@app/lib/casl";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors"; import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { UnpackedPermissionSchema } from "@app/server/routes/santizedSchemas/permission"; import { UnpackedPermissionSchema } from "@app/server/routes/sanitizedSchema/permission";
import { ActorType } from "@app/services/auth/auth-type"; import { ActorType } from "@app/services/auth/auth-type";
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal"; import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
@@ -1,29 +1,15 @@
import { ForbiddenError } from "@casl/ability"; import { ForbiddenError } from "@casl/ability";
import jwt from "jsonwebtoken"; import jwt from "jsonwebtoken";
import { import { OrgMembershipStatus, TableName, TSamlConfigs, TSamlConfigsUpdate, TUsers } from "@app/db/schemas";
OrgMembershipStatus,
SecretKeyEncoding,
TableName,
TSamlConfigs,
TSamlConfigsUpdate,
TUsers
} from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import {
decryptSymmetric,
encryptSymmetric,
generateAsymmetricKeyPair,
generateSymmetricKey,
infisicalSymmetricDecrypt,
infisicalSymmetricEncypt
} from "@app/lib/crypto/encryption";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors"; import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { AuthTokenType } from "@app/services/auth/auth-type"; import { AuthTokenType } 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 { TokenType } from "@app/services/auth-token/auth-token-types"; import { TokenType } from "@app/services/auth-token/auth-token-types";
import { TIdentityMetadataDALFactory } from "@app/services/identity/identity-metadata-dal"; import { TIdentityMetadataDALFactory } from "@app/services/identity/identity-metadata-dal";
import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal"; import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { KmsDataKey } from "@app/services/kms/kms-types";
import { TOrgDALFactory } from "@app/services/org/org-dal"; import { TOrgDALFactory } from "@app/services/org/org-dal";
import { getDefaultOrgMembershipRole } from "@app/services/org/org-role-fns"; import { getDefaultOrgMembershipRole } from "@app/services/org/org-role-fns";
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal"; import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
@@ -52,21 +38,19 @@ type TSamlConfigServiceFactoryDep = {
TOrgDALFactory, TOrgDALFactory,
"createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById" "createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById"
>; >;
identityMetadataDAL: Pick<TIdentityMetadataDALFactory, "delete" | "insertMany" | "transaction">; identityMetadataDAL: Pick<TIdentityMetadataDALFactory, "delete" | "insertMany" | "transaction">;
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "create">; orgMembershipDAL: Pick<TOrgMembershipDALFactory, "create">;
orgBotDAL: Pick<TOrgBotDALFactory, "findOne" | "create" | "transaction">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">; permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan" | "updateSubscriptionOrgMemberCount">; licenseService: Pick<TLicenseServiceFactory, "getPlan" | "updateSubscriptionOrgMemberCount">;
tokenService: Pick<TAuthTokenServiceFactory, "createTokenForUser">; tokenService: Pick<TAuthTokenServiceFactory, "createTokenForUser">;
smtpService: Pick<TSmtpService, "sendMail">; smtpService: Pick<TSmtpService, "sendMail">;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
}; };
export type TSamlConfigServiceFactory = ReturnType<typeof samlConfigServiceFactory>; export type TSamlConfigServiceFactory = ReturnType<typeof samlConfigServiceFactory>;
export const samlConfigServiceFactory = ({ export const samlConfigServiceFactory = ({
samlConfigDAL, samlConfigDAL,
orgBotDAL,
orgDAL, orgDAL,
orgMembershipDAL, orgMembershipDAL,
userDAL, userDAL,
@@ -75,7 +59,8 @@ export const samlConfigServiceFactory = ({
licenseService, licenseService,
tokenService, tokenService,
smtpService, smtpService,
identityMetadataDAL identityMetadataDAL,
kmsService
}: TSamlConfigServiceFactoryDep) => { }: TSamlConfigServiceFactoryDep) => {
const createSamlCfg = async ({ const createSamlCfg = async ({
cert, cert,
@@ -99,70 +84,18 @@ export const samlConfigServiceFactory = ({
"Failed to create SAML SSO configuration due to plan restriction. Upgrade plan to create SSO configuration." "Failed to create SAML SSO configuration due to plan restriction. Upgrade plan to create SSO configuration."
}); });
const orgBot = await orgBotDAL.transaction(async (tx) => { const { encryptor } = await kmsService.createCipherPairWithDataKey({
const doc = await orgBotDAL.findOne({ orgId }, tx); type: KmsDataKey.Organization,
if (doc) return doc; orgId
const { privateKey, publicKey } = generateAsymmetricKeyPair();
const key = generateSymmetricKey();
const {
ciphertext: encryptedPrivateKey,
iv: privateKeyIV,
tag: privateKeyTag,
encoding: privateKeyKeyEncoding,
algorithm: privateKeyAlgorithm
} = infisicalSymmetricEncypt(privateKey);
const {
ciphertext: encryptedSymmetricKey,
iv: symmetricKeyIV,
tag: symmetricKeyTag,
encoding: symmetricKeyKeyEncoding,
algorithm: symmetricKeyAlgorithm
} = infisicalSymmetricEncypt(key);
return orgBotDAL.create(
{
name: "Infisical org bot",
publicKey,
privateKeyIV,
encryptedPrivateKey,
symmetricKeyIV,
symmetricKeyTag,
encryptedSymmetricKey,
symmetricKeyAlgorithm,
orgId,
privateKeyTag,
privateKeyAlgorithm,
privateKeyKeyEncoding,
symmetricKeyKeyEncoding
},
tx
);
}); });
const key = infisicalSymmetricDecrypt({
ciphertext: orgBot.encryptedSymmetricKey,
iv: orgBot.symmetricKeyIV,
tag: orgBot.symmetricKeyTag,
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
});
const { ciphertext: encryptedEntryPoint, iv: entryPointIV, tag: entryPointTag } = encryptSymmetric(entryPoint, key);
const { ciphertext: encryptedIssuer, iv: issuerIV, tag: issuerTag } = encryptSymmetric(issuer, key);
const { ciphertext: encryptedCert, iv: certIV, tag: certTag } = encryptSymmetric(cert, key);
const samlConfig = await samlConfigDAL.create({ const samlConfig = await samlConfigDAL.create({
orgId, orgId,
authProvider, authProvider,
isActive, isActive,
encryptedEntryPoint, encryptedSamlIssuer: encryptor({ plainText: Buffer.from(issuer) }).cipherTextBlob,
entryPointIV, encryptedSamlEntryPoint: encryptor({ plainText: Buffer.from(entryPoint) }).cipherTextBlob,
entryPointTag, encryptedSamlCertificate: encryptor({ plainText: Buffer.from(cert) }).cipherTextBlob
encryptedIssuer,
issuerIV,
issuerTag,
encryptedCert,
certIV,
certTag
}); });
return samlConfig; return samlConfig;
@@ -190,40 +123,21 @@ export const samlConfigServiceFactory = ({
}); });
const updateQuery: TSamlConfigsUpdate = { authProvider, isActive, lastUsed: null }; const updateQuery: TSamlConfigsUpdate = { authProvider, isActive, lastUsed: null };
const orgBot = await orgBotDAL.findOne({ orgId }); const { encryptor } = await kmsService.createCipherPairWithDataKey({
if (!orgBot) type: KmsDataKey.Organization,
throw new NotFoundError({ orgId
message: `Organization bot not found for organization with ID '${orgId}'`,
name: "OrgBotNotFound"
});
const key = infisicalSymmetricDecrypt({
ciphertext: orgBot.encryptedSymmetricKey,
iv: orgBot.symmetricKeyIV,
tag: orgBot.symmetricKeyTag,
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
}); });
if (entryPoint !== undefined) { if (entryPoint !== undefined) {
const { updateQuery.encryptedSamlEntryPoint = encryptor({ plainText: Buffer.from(entryPoint) }).cipherTextBlob;
ciphertext: encryptedEntryPoint,
iv: entryPointIV,
tag: entryPointTag
} = encryptSymmetric(entryPoint, key);
updateQuery.encryptedEntryPoint = encryptedEntryPoint;
updateQuery.entryPointIV = entryPointIV;
updateQuery.entryPointTag = entryPointTag;
} }
if (issuer !== undefined) { if (issuer !== undefined) {
const { ciphertext: encryptedIssuer, iv: issuerIV, tag: issuerTag } = encryptSymmetric(issuer, key); updateQuery.encryptedSamlIssuer = encryptor({ plainText: Buffer.from(issuer) }).cipherTextBlob;
updateQuery.encryptedIssuer = encryptedIssuer;
updateQuery.issuerIV = issuerIV;
updateQuery.issuerTag = issuerTag;
} }
if (cert !== undefined) { if (cert !== undefined) {
const { ciphertext: encryptedCert, iv: certIV, tag: certTag } = encryptSymmetric(cert, key); updateQuery.encryptedSamlCertificate = encryptor({ plainText: Buffer.from(cert) }).cipherTextBlob;
updateQuery.encryptedCert = encryptedCert;
updateQuery.certIV = certIV;
updateQuery.certTag = certTag;
} }
const [ssoConfig] = await samlConfigDAL.update({ orgId }, updateQuery); const [ssoConfig] = await samlConfigDAL.update({ orgId }, updateQuery);
@@ -233,14 +147,14 @@ export const samlConfigServiceFactory = ({
}; };
const getSaml = async (dto: TGetSamlCfgDTO) => { const getSaml = async (dto: TGetSamlCfgDTO) => {
let ssoConfig: TSamlConfigs | undefined; let samlConfig: TSamlConfigs | undefined;
if (dto.type === "org") { if (dto.type === "org") {
ssoConfig = await samlConfigDAL.findOne({ orgId: dto.orgId }); samlConfig = await samlConfigDAL.findOne({ orgId: dto.orgId });
if (!ssoConfig) return; if (!samlConfig) return;
} else if (dto.type === "orgSlug") { } else if (dto.type === "orgSlug") {
const org = await orgDAL.findOne({ slug: dto.orgSlug }); const org = await orgDAL.findOne({ slug: dto.orgSlug });
if (!org) return; if (!org) return;
ssoConfig = await samlConfigDAL.findOne({ orgId: org.id }); samlConfig = await samlConfigDAL.findOne({ orgId: org.id });
} else if (dto.type === "ssoId") { } else if (dto.type === "ssoId") {
// TODO: // TODO:
// We made this change because saml config ids were not moved over during the migration // We made this change because saml config ids were not moved over during the migration
@@ -259,81 +173,51 @@ export const samlConfigServiceFactory = ({
const id = UUIDToMongoId[dto.id] ?? dto.id; const id = UUIDToMongoId[dto.id] ?? dto.id;
ssoConfig = await samlConfigDAL.findById(id); samlConfig = await samlConfigDAL.findById(id);
} }
if (!ssoConfig) throw new NotFoundError({ message: `Failed to find SSO data` }); if (!samlConfig) throw new NotFoundError({ message: `Failed to find SSO data` });
// when dto is type id means it's internally used // when dto is type id means it's internally used
if (dto.type === "org") { if (dto.type === "org") {
const { permission } = await permissionService.getOrgPermission( const { permission } = await permissionService.getOrgPermission(
dto.actor, dto.actor,
dto.actorId, dto.actorId,
ssoConfig.orgId, samlConfig.orgId,
dto.actorAuthMethod, dto.actorAuthMethod,
dto.actorOrgId dto.actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Sso); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Sso);
} }
const { const { decryptor } = await kmsService.createCipherPairWithDataKey({
entryPointTag, type: KmsDataKey.Organization,
entryPointIV, orgId: samlConfig.orgId
encryptedEntryPoint,
certTag,
certIV,
encryptedCert,
issuerTag,
issuerIV,
encryptedIssuer
} = ssoConfig;
const orgBot = await orgBotDAL.findOne({ orgId: ssoConfig.orgId });
if (!orgBot)
throw new NotFoundError({
message: `Organization bot not found in organization with ID '${ssoConfig.orgId}'`,
name: "OrgBotNotFound"
});
const key = infisicalSymmetricDecrypt({
ciphertext: orgBot.encryptedSymmetricKey,
iv: orgBot.symmetricKeyIV,
tag: orgBot.symmetricKeyTag,
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
}); });
let entryPoint = ""; let entryPoint = "";
if (encryptedEntryPoint && entryPointIV && entryPointTag) { if (samlConfig.encryptedSamlEntryPoint) {
entryPoint = decryptSymmetric({ entryPoint = decryptor({ cipherTextBlob: samlConfig.encryptedSamlEntryPoint }).toString();
ciphertext: encryptedEntryPoint,
key,
tag: entryPointTag,
iv: entryPointIV
});
} }
let issuer = ""; let issuer = "";
if (encryptedIssuer && issuerTag && issuerIV) { if (samlConfig.encryptedSamlIssuer) {
issuer = decryptSymmetric({ issuer = decryptor({ cipherTextBlob: samlConfig.encryptedSamlIssuer }).toString();
key,
tag: issuerTag,
iv: issuerIV,
ciphertext: encryptedIssuer
});
} }
let cert = ""; let cert = "";
if (encryptedCert && certTag && certIV) { if (samlConfig.encryptedSamlCertificate) {
cert = decryptSymmetric({ key, tag: certTag, iv: certIV, ciphertext: encryptedCert }); cert = decryptor({ cipherTextBlob: samlConfig.encryptedSamlCertificate }).toString();
} }
return { return {
id: ssoConfig.id, id: samlConfig.id,
organization: ssoConfig.orgId, organization: samlConfig.orgId,
orgId: ssoConfig.orgId, orgId: samlConfig.orgId,
authProvider: ssoConfig.authProvider, authProvider: samlConfig.authProvider,
isActive: ssoConfig.isActive, isActive: samlConfig.isActive,
entryPoint, entryPoint,
issuer, issuer,
cert, cert,
lastUsed: ssoConfig.lastUsed lastUsed: samlConfig.lastUsed
}; };
}; };
+8 -8
View File
@@ -451,7 +451,6 @@ export const registerRoutes = async (
const samlService = samlConfigServiceFactory({ const samlService = samlConfigServiceFactory({
identityMetadataDAL, identityMetadataDAL,
permissionService, permissionService,
orgBotDAL,
orgDAL, orgDAL,
orgMembershipDAL, orgMembershipDAL,
userDAL, userDAL,
@@ -459,7 +458,8 @@ export const registerRoutes = async (
samlConfigDAL, samlConfigDAL,
licenseService, licenseService,
tokenService, tokenService,
smtpService smtpService,
kmsService
}); });
const groupService = groupServiceFactory({ const groupService = groupServiceFactory({
userDAL, userDAL,
@@ -510,7 +510,6 @@ export const registerRoutes = async (
ldapGroupMapDAL, ldapGroupMapDAL,
orgDAL, orgDAL,
orgMembershipDAL, orgMembershipDAL,
orgBotDAL,
groupDAL, groupDAL,
groupProjectDAL, groupProjectDAL,
projectKeyDAL, projectKeyDAL,
@@ -522,7 +521,8 @@ export const registerRoutes = async (
permissionService, permissionService,
licenseService, licenseService,
tokenService, tokenService,
smtpService smtpService,
kmsService
}); });
const telemetryService = telemetryServiceFactory({ const telemetryService = telemetryServiceFactory({
@@ -1244,9 +1244,9 @@ export const registerRoutes = async (
identityKubernetesAuthDAL, identityKubernetesAuthDAL,
identityOrgMembershipDAL, identityOrgMembershipDAL,
identityAccessTokenDAL, identityAccessTokenDAL,
orgBotDAL,
permissionService, permissionService,
licenseService licenseService,
kmsService
}); });
const identityGcpAuthService = identityGcpAuthServiceFactory({ const identityGcpAuthService = identityGcpAuthServiceFactory({
identityGcpAuthDAL, identityGcpAuthDAL,
@@ -1278,7 +1278,7 @@ export const registerRoutes = async (
identityAccessTokenDAL, identityAccessTokenDAL,
permissionService, permissionService,
licenseService, licenseService,
orgBotDAL kmsService
}); });
const identityJwtAuthService = identityJwtAuthServiceFactory({ const identityJwtAuthService = identityJwtAuthServiceFactory({
@@ -1347,7 +1347,7 @@ export const registerRoutes = async (
licenseService, licenseService,
tokenService, tokenService,
smtpService, smtpService,
orgBotDAL, kmsService,
permissionService, permissionService,
oidcConfigDAL, oidcConfigDAL,
projectBotDAL, projectBotDAL,
@@ -0,0 +1,42 @@
import { LdapConfigsSchema, OidcConfigsSchema, SamlConfigsSchema } from "@app/db/schemas";
export const SanitizedSamlConfigSchema = SamlConfigsSchema.pick({
id: true,
orgId: true,
isActive: true,
lastUsed: true,
createdAt: true,
updatedAt: true,
authProvider: true
});
export const SanitizedLdapConfigSchema = LdapConfigsSchema.pick({
updatedAt: true,
createdAt: true,
isActive: true,
orgId: true,
id: true,
url: true,
searchBase: true,
searchFilter: true,
groupSearchBase: true,
uniqueUserAttribute: true,
groupSearchFilter: true
});
export const SanitizedOidcConfigSchema = OidcConfigsSchema.pick({
id: true,
orgId: true,
isActive: true,
createdAt: true,
updatedAt: true,
lastUsed: true,
issuer: true,
jwksUri: true,
discoveryURL: true,
tokenEndpoint: true,
userinfoEndpoint: true,
configurationType: true,
allowedEmailDomains: true,
authorizationEndpoint: true
});
@@ -11,7 +11,7 @@ import {
} from "@app/db/schemas"; } from "@app/db/schemas";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { UnpackedPermissionSchema } from "./santizedSchemas/permission"; import { UnpackedPermissionSchema } from "./sanitizedSchema/permission";
// sometimes the return data must be santizied to avoid leaking important values // sometimes the return data must be santizied to avoid leaking important values
// always prefer pick over omit in zod // always prefer pick over omit in zod
@@ -201,7 +201,12 @@ export const SanitizedRoleSchemaV1 = ProjectRolesSchema.extend({
}); });
export const SanitizedDynamicSecretSchema = DynamicSecretsSchema.omit({ export const SanitizedDynamicSecretSchema = DynamicSecretsSchema.omit({
encryptedInput: true encryptedInput: true,
keyEncoding: true,
inputCiphertext: true,
inputIV: true,
inputTag: true,
algorithm: true
}); });
export const SanitizedAuditLogStreamSchema = z.object({ export const SanitizedAuditLogStreamSchema = z.object({
@@ -8,13 +8,19 @@ import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
import { TIdentityTrustedIp } from "@app/services/identity/identity-types"; import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
const IdentityKubernetesAuthResponseSchema = IdentityKubernetesAuthsSchema.omit({ const IdentityKubernetesAuthResponseSchema = IdentityKubernetesAuthsSchema.pick({
encryptedCaCert: true, id: true,
caCertIV: true, accessTokenTTL: true,
caCertTag: true, accessTokenMaxTTL: true,
encryptedTokenReviewerJwt: true, accessTokenNumUsesLimit: true,
tokenReviewerJwtIV: true, accessTokenTrustedIps: true,
tokenReviewerJwtTag: true createdAt: true,
updatedAt: true,
identityId: true,
kubernetesHost: true,
allowedNamespaces: true,
allowedNames: true,
allowedAudience: true
}).extend({ }).extend({
caCert: z.string(), caCert: z.string(),
tokenReviewerJwt: z.string() tokenReviewerJwt: z.string()
@@ -13,9 +13,19 @@ import {
} from "@app/services/identity-oidc-auth/identity-oidc-auth-validators"; } from "@app/services/identity-oidc-auth/identity-oidc-auth-validators";
const IdentityOidcAuthResponseSchema = IdentityOidcAuthsSchema.omit({ const IdentityOidcAuthResponseSchema = IdentityOidcAuthsSchema.omit({
encryptedCaCert: true, id: true,
caCertIV: true, accessTokenTTL: true,
caCertTag: true accessTokenMaxTTL: true,
accessTokenNumUsesLimit: true,
accessTokenTrustedIps: true,
identityId: true,
oidcDiscoveryUrl: true,
boundIssuer: true,
boundAudiences: true,
boundClaims: true,
boundSubject: true,
createdAt: true,
updatedAt: true
}).extend({ }).extend({
caCert: z.string() caCert: z.string()
}); });
@@ -3,28 +3,21 @@ import axios, { AxiosError } from "axios";
import https from "https"; import https from "https";
import jwt from "jsonwebtoken"; import jwt from "jsonwebtoken";
import { IdentityAuthMethod, SecretKeyEncoding, TIdentityKubernetesAuthsUpdate } from "@app/db/schemas"; import { IdentityAuthMethod, TIdentityKubernetesAuthsUpdate } from "@app/db/schemas";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { isAtLeastAsPrivileged } from "@app/lib/casl"; import { isAtLeastAsPrivileged } from "@app/lib/casl";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import {
decryptSymmetric,
encryptSymmetric,
generateAsymmetricKeyPair,
generateSymmetricKey,
infisicalSymmetricDecrypt,
infisicalSymmetricEncypt
} from "@app/lib/crypto/encryption";
import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors"; import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors";
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip"; import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal";
import { ActorType, AuthTokenType } from "../auth/auth-type"; import { ActorType, AuthTokenType } from "../auth/auth-type";
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal"; import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal"; import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types"; import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
import { TKmsServiceFactory } from "../kms/kms-service";
import { KmsDataKey } from "../kms/kms-types";
import { TIdentityKubernetesAuthDALFactory } from "./identity-kubernetes-auth-dal"; import { TIdentityKubernetesAuthDALFactory } from "./identity-kubernetes-auth-dal";
import { extractK8sUsername } from "./identity-kubernetes-auth-fns"; import { extractK8sUsername } from "./identity-kubernetes-auth-fns";
import { import {
@@ -43,9 +36,9 @@ type TIdentityKubernetesAuthServiceFactoryDep = {
>; >;
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">; identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">;
identityOrgMembershipDAL: Pick<TIdentityOrgDALFactory, "findOne" | "findById">; identityOrgMembershipDAL: Pick<TIdentityOrgDALFactory, "findOne" | "findById">;
orgBotDAL: Pick<TOrgBotDALFactory, "findOne" | "transaction" | "create">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">; permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">; licenseService: Pick<TLicenseServiceFactory, "getPlan">;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
}; };
export type TIdentityKubernetesAuthServiceFactory = ReturnType<typeof identityKubernetesAuthServiceFactory>; export type TIdentityKubernetesAuthServiceFactory = ReturnType<typeof identityKubernetesAuthServiceFactory>;
@@ -54,9 +47,9 @@ export const identityKubernetesAuthServiceFactory = ({
identityKubernetesAuthDAL, identityKubernetesAuthDAL,
identityOrgMembershipDAL, identityOrgMembershipDAL,
identityAccessTokenDAL, identityAccessTokenDAL,
orgBotDAL,
permissionService, permissionService,
licenseService licenseService,
kmsService
}: TIdentityKubernetesAuthServiceFactoryDep) => { }: TIdentityKubernetesAuthServiceFactoryDep) => {
const login = async ({ identityId, jwt: serviceAccountJwt }: TLoginKubernetesAuthDTO) => { const login = async ({ identityId, jwt: serviceAccountJwt }: TLoginKubernetesAuthDTO) => {
const identityKubernetesAuth = await identityKubernetesAuthDAL.findOne({ identityId }); const identityKubernetesAuth = await identityKubernetesAuthDAL.findOne({ identityId });
@@ -75,42 +68,21 @@ export const identityKubernetesAuthServiceFactory = ({
}); });
} }
const orgBot = await orgBotDAL.findOne({ orgId: identityMembershipOrg.orgId }); const { decryptor } = await kmsService.createCipherPairWithDataKey({
if (!orgBot) { type: KmsDataKey.Organization,
throw new NotFoundError({ orgId: identityMembershipOrg.orgId
message: `Organization bot not found for organization with ID ${identityMembershipOrg.orgId}`,
name: "OrgBotNotFound"
});
}
const key = infisicalSymmetricDecrypt({
ciphertext: orgBot.encryptedSymmetricKey,
iv: orgBot.symmetricKeyIV,
tag: orgBot.symmetricKeyTag,
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
}); });
const { encryptedCaCert, caCertIV, caCertTag, encryptedTokenReviewerJwt, tokenReviewerJwtIV, tokenReviewerJwtTag } =
identityKubernetesAuth;
let caCert = ""; let caCert = "";
if (encryptedCaCert && caCertIV && caCertTag) { if (identityKubernetesAuth.encryptedKubernetesCaCertificate) {
caCert = decryptSymmetric({ caCert = decryptor({ cipherTextBlob: identityKubernetesAuth.encryptedKubernetesCaCertificate }).toString();
ciphertext: encryptedCaCert,
iv: caCertIV,
tag: caCertTag,
key
});
} }
let tokenReviewerJwt = ""; let tokenReviewerJwt = "";
if (encryptedTokenReviewerJwt && tokenReviewerJwtIV && tokenReviewerJwtTag) { if (identityKubernetesAuth.encryptedKubernetesTokenReviewerJwt) {
tokenReviewerJwt = decryptSymmetric({ tokenReviewerJwt = decryptor({
ciphertext: encryptedTokenReviewerJwt, cipherTextBlob: identityKubernetesAuth.encryptedKubernetesTokenReviewerJwt
iv: tokenReviewerJwtIV, }).toString();
tag: tokenReviewerJwtTag,
key
});
} }
const { data } = await axios const { data } = await axios
@@ -297,79 +269,25 @@ export const identityKubernetesAuthServiceFactory = ({
return extractIPDetails(accessTokenTrustedIp.ipAddress); return extractIPDetails(accessTokenTrustedIp.ipAddress);
}); });
const orgBot = await orgBotDAL.transaction(async (tx) => { const { encryptor } = await kmsService.createCipherPairWithDataKey({
const doc = await orgBotDAL.findOne({ orgId: identityMembershipOrg.orgId }, tx); type: KmsDataKey.Organization,
if (doc) return doc; orgId: identityMembershipOrg.orgId
const { privateKey, publicKey } = generateAsymmetricKeyPair();
const key = generateSymmetricKey();
const {
ciphertext: encryptedPrivateKey,
iv: privateKeyIV,
tag: privateKeyTag,
encoding: privateKeyKeyEncoding,
algorithm: privateKeyAlgorithm
} = infisicalSymmetricEncypt(privateKey);
const {
ciphertext: encryptedSymmetricKey,
iv: symmetricKeyIV,
tag: symmetricKeyTag,
encoding: symmetricKeyKeyEncoding,
algorithm: symmetricKeyAlgorithm
} = infisicalSymmetricEncypt(key);
return orgBotDAL.create(
{
name: "Infisical org bot",
publicKey,
privateKeyIV,
encryptedPrivateKey,
symmetricKeyIV,
symmetricKeyTag,
encryptedSymmetricKey,
symmetricKeyAlgorithm,
orgId: identityMembershipOrg.orgId,
privateKeyTag,
privateKeyAlgorithm,
privateKeyKeyEncoding,
symmetricKeyKeyEncoding
},
tx
);
}); });
const key = infisicalSymmetricDecrypt({
ciphertext: orgBot.encryptedSymmetricKey,
iv: orgBot.symmetricKeyIV,
tag: orgBot.symmetricKeyTag,
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
});
const { ciphertext: encryptedCaCert, iv: caCertIV, tag: caCertTag } = encryptSymmetric(caCert, key);
const {
ciphertext: encryptedTokenReviewerJwt,
iv: tokenReviewerJwtIV,
tag: tokenReviewerJwtTag
} = encryptSymmetric(tokenReviewerJwt, key);
const identityKubernetesAuth = await identityKubernetesAuthDAL.transaction(async (tx) => { const identityKubernetesAuth = await identityKubernetesAuthDAL.transaction(async (tx) => {
const doc = await identityKubernetesAuthDAL.create( const doc = await identityKubernetesAuthDAL.create(
{ {
identityId: identityMembershipOrg.identityId, identityId: identityMembershipOrg.identityId,
kubernetesHost, kubernetesHost,
encryptedCaCert,
caCertIV,
caCertTag,
encryptedTokenReviewerJwt,
tokenReviewerJwtIV,
tokenReviewerJwtTag,
allowedNamespaces, allowedNamespaces,
allowedNames, allowedNames,
allowedAudience, allowedAudience,
accessTokenMaxTTL, accessTokenMaxTTL,
accessTokenTTL, accessTokenTTL,
accessTokenNumUsesLimit, accessTokenNumUsesLimit,
accessTokenTrustedIps: JSON.stringify(reformattedAccessTokenTrustedIps) accessTokenTrustedIps: JSON.stringify(reformattedAccessTokenTrustedIps),
encryptedKubernetesTokenReviewerJwt: encryptor({ plainText: Buffer.from(tokenReviewerJwt) }).cipherTextBlob,
encryptedKubernetesCaCertificate: encryptor({ plainText: Buffer.from(caCert) }).cipherTextBlob
}, },
tx tx
); );
@@ -455,61 +373,34 @@ export const identityKubernetesAuthServiceFactory = ({
: undefined : undefined
}; };
const orgBot = await orgBotDAL.findOne({ orgId: identityMembershipOrg.orgId }); const { encryptor, decryptor } = await kmsService.createCipherPairWithDataKey({
if (!orgBot) { type: KmsDataKey.Organization,
throw new NotFoundError({ orgId: identityMembershipOrg.orgId
message: `Organization bot not found for organization with ID ${identityMembershipOrg.orgId}`,
name: "OrgBotNotFound"
});
}
const key = infisicalSymmetricDecrypt({
ciphertext: orgBot.encryptedSymmetricKey,
iv: orgBot.symmetricKeyIV,
tag: orgBot.symmetricKeyTag,
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
}); });
if (caCert !== undefined) { if (caCert !== undefined) {
const { ciphertext: encryptedCACert, iv: caCertIV, tag: caCertTag } = encryptSymmetric(caCert, key); updateQuery.encryptedKubernetesCaCertificate = encryptor({ plainText: Buffer.from(caCert) }).cipherTextBlob;
updateQuery.encryptedCaCert = encryptedCACert;
updateQuery.caCertIV = caCertIV;
updateQuery.caCertTag = caCertTag;
} }
if (tokenReviewerJwt !== undefined) { if (tokenReviewerJwt !== undefined) {
const { updateQuery.encryptedKubernetesTokenReviewerJwt = encryptor({
ciphertext: encryptedTokenReviewerJwt, plainText: Buffer.from(tokenReviewerJwt)
iv: tokenReviewerJwtIV, }).cipherTextBlob;
tag: tokenReviewerJwtTag
} = encryptSymmetric(tokenReviewerJwt, key);
updateQuery.encryptedTokenReviewerJwt = encryptedTokenReviewerJwt;
updateQuery.tokenReviewerJwtIV = tokenReviewerJwtIV;
updateQuery.tokenReviewerJwtTag = tokenReviewerJwtTag;
} }
const updatedKubernetesAuth = await identityKubernetesAuthDAL.updateById(identityKubernetesAuth.id, updateQuery); const updatedKubernetesAuth = await identityKubernetesAuthDAL.updateById(identityKubernetesAuth.id, updateQuery);
const updatedCACert = const updatedCACert = updatedKubernetesAuth.encryptedKubernetesCaCertificate
updatedKubernetesAuth.encryptedCaCert && updatedKubernetesAuth.caCertIV && updatedKubernetesAuth.caCertTag ? decryptor({
? decryptSymmetric({ cipherTextBlob: updatedKubernetesAuth.encryptedKubernetesCaCertificate
ciphertext: updatedKubernetesAuth.encryptedCaCert, }).toString()
iv: updatedKubernetesAuth.caCertIV, : "";
tag: updatedKubernetesAuth.caCertTag,
key
})
: "";
const updatedTokenReviewerJwt = const updatedTokenReviewerJwt = updatedKubernetesAuth.encryptedKubernetesTokenReviewerJwt
updatedKubernetesAuth.encryptedTokenReviewerJwt && ? decryptor({
updatedKubernetesAuth.tokenReviewerJwtIV && cipherTextBlob: updatedKubernetesAuth.encryptedKubernetesTokenReviewerJwt
updatedKubernetesAuth.tokenReviewerJwtTag }).toString()
? decryptSymmetric({ : "";
ciphertext: updatedKubernetesAuth.encryptedTokenReviewerJwt,
iv: updatedKubernetesAuth.tokenReviewerJwtIV,
tag: updatedKubernetesAuth.tokenReviewerJwtTag,
key
})
: "";
return { return {
...updatedKubernetesAuth, ...updatedKubernetesAuth,
@@ -545,41 +436,21 @@ export const identityKubernetesAuthServiceFactory = ({
); );
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Identity); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Identity);
const orgBot = await orgBotDAL.findOne({ orgId: identityMembershipOrg.orgId }); const { decryptor } = await kmsService.createCipherPairWithDataKey({
if (!orgBot) type: KmsDataKey.Organization,
throw new NotFoundError({ orgId: identityMembershipOrg.orgId
message: `Organization bot not found for organization with ID ${identityMembershipOrg.orgId}`,
name: "OrgBotNotFound"
});
const key = infisicalSymmetricDecrypt({
ciphertext: orgBot.encryptedSymmetricKey,
iv: orgBot.symmetricKeyIV,
tag: orgBot.symmetricKeyTag,
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
}); });
const { encryptedCaCert, caCertIV, caCertTag, encryptedTokenReviewerJwt, tokenReviewerJwtIV, tokenReviewerJwtTag } =
identityKubernetesAuth;
let caCert = ""; let caCert = "";
if (encryptedCaCert && caCertIV && caCertTag) { if (identityKubernetesAuth.encryptedKubernetesCaCertificate) {
caCert = decryptSymmetric({ caCert = decryptor({ cipherTextBlob: identityKubernetesAuth.encryptedKubernetesCaCertificate }).toString();
ciphertext: encryptedCaCert,
iv: caCertIV,
tag: caCertTag,
key
});
} }
let tokenReviewerJwt = ""; let tokenReviewerJwt = "";
if (encryptedTokenReviewerJwt && tokenReviewerJwtIV && tokenReviewerJwtTag) { if (identityKubernetesAuth.encryptedKubernetesTokenReviewerJwt) {
tokenReviewerJwt = decryptSymmetric({ tokenReviewerJwt = decryptor({
ciphertext: encryptedTokenReviewerJwt, cipherTextBlob: identityKubernetesAuth.encryptedKubernetesTokenReviewerJwt
iv: tokenReviewerJwtIV, }).toString();
tag: tokenReviewerJwtTag,
key
});
} }
return { ...identityKubernetesAuth, caCert, tokenReviewerJwt, orgId: identityMembershipOrg.orgId }; return { ...identityKubernetesAuth, caCert, tokenReviewerJwt, orgId: identityMembershipOrg.orgId };
@@ -4,20 +4,12 @@ import https from "https";
import jwt from "jsonwebtoken"; import jwt from "jsonwebtoken";
import { JwksClient } from "jwks-rsa"; import { JwksClient } from "jwks-rsa";
import { IdentityAuthMethod, SecretKeyEncoding, TIdentityOidcAuthsUpdate } from "@app/db/schemas"; import { IdentityAuthMethod, TIdentityOidcAuthsUpdate } from "@app/db/schemas";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { isAtLeastAsPrivileged } from "@app/lib/casl"; import { isAtLeastAsPrivileged } from "@app/lib/casl";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { generateAsymmetricKeyPair } from "@app/lib/crypto";
import {
decryptSymmetric,
encryptSymmetric,
generateSymmetricKey,
infisicalSymmetricDecrypt,
infisicalSymmetricEncypt
} from "@app/lib/crypto/encryption";
import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors"; import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors";
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip"; import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
@@ -25,7 +17,8 @@ import { ActorType, AuthTokenType } from "../auth/auth-type";
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal"; import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal"; import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types"; import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
import { TOrgBotDALFactory } from "../org/org-bot-dal"; import { TKmsServiceFactory } from "../kms/kms-service";
import { KmsDataKey } from "../kms/kms-types";
import { TIdentityOidcAuthDALFactory } from "./identity-oidc-auth-dal"; import { TIdentityOidcAuthDALFactory } from "./identity-oidc-auth-dal";
import { doesAudValueMatchOidcPolicy, doesFieldValueMatchOidcPolicy } from "./identity-oidc-auth-fns"; import { doesAudValueMatchOidcPolicy, doesFieldValueMatchOidcPolicy } from "./identity-oidc-auth-fns";
import { import {
@@ -42,7 +35,7 @@ type TIdentityOidcAuthServiceFactoryDep = {
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">; identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">; permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">; licenseService: Pick<TLicenseServiceFactory, "getPlan">;
orgBotDAL: Pick<TOrgBotDALFactory, "findOne" | "transaction" | "create">; kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
}; };
export type TIdentityOidcAuthServiceFactory = ReturnType<typeof identityOidcAuthServiceFactory>; export type TIdentityOidcAuthServiceFactory = ReturnType<typeof identityOidcAuthServiceFactory>;
@@ -53,7 +46,7 @@ export const identityOidcAuthServiceFactory = ({
permissionService, permissionService,
licenseService, licenseService,
identityAccessTokenDAL, identityAccessTokenDAL,
orgBotDAL kmsService
}: TIdentityOidcAuthServiceFactoryDep) => { }: TIdentityOidcAuthServiceFactoryDep) => {
const login = async ({ identityId, jwt: oidcJwt }: TLoginOidcAuthDTO) => { const login = async ({ identityId, jwt: oidcJwt }: TLoginOidcAuthDTO) => {
const identityOidcAuth = await identityOidcAuthDAL.findOne({ identityId }); const identityOidcAuth = await identityOidcAuthDAL.findOne({ identityId });
@@ -70,31 +63,14 @@ export const identityOidcAuthServiceFactory = ({
}); });
} }
const orgBot = await orgBotDAL.findOne({ orgId: identityMembershipOrg.orgId }); const { decryptor } = await kmsService.createCipherPairWithDataKey({
if (!orgBot) { type: KmsDataKey.Organization,
throw new NotFoundError({ orgId: identityMembershipOrg.orgId
message: `Organization bot not found for organization with ID '${identityMembershipOrg.orgId}'`,
name: "OrgBotNotFound"
});
}
const key = infisicalSymmetricDecrypt({
ciphertext: orgBot.encryptedSymmetricKey,
iv: orgBot.symmetricKeyIV,
tag: orgBot.symmetricKeyTag,
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
}); });
const { encryptedCaCert, caCertIV, caCertTag } = identityOidcAuth;
let caCert = ""; let caCert = "";
if (encryptedCaCert && caCertIV && caCertTag) { if (identityOidcAuth.encryptedCaCertificate) {
caCert = decryptSymmetric({ caCert = decryptor({ cipherTextBlob: identityOidcAuth.encryptedCaCertificate }).toString();
ciphertext: encryptedCaCert,
iv: caCertIV,
tag: caCertTag,
key
});
} }
const requestAgent = new https.Agent({ ca: caCert, rejectUnauthorized: !!caCert }); const requestAgent = new https.Agent({ ca: caCert, rejectUnauthorized: !!caCert });
@@ -264,64 +240,17 @@ export const identityOidcAuthServiceFactory = ({
return extractIPDetails(accessTokenTrustedIp.ipAddress); return extractIPDetails(accessTokenTrustedIp.ipAddress);
}); });
const orgBot = await orgBotDAL.transaction(async (tx) => { const { encryptor } = await kmsService.createCipherPairWithDataKey({
const doc = await orgBotDAL.findOne({ orgId: identityMembershipOrg.orgId }, tx); type: KmsDataKey.Organization,
if (doc) return doc; orgId: identityMembershipOrg.orgId
const { privateKey, publicKey } = generateAsymmetricKeyPair();
const key = generateSymmetricKey();
const {
ciphertext: encryptedPrivateKey,
iv: privateKeyIV,
tag: privateKeyTag,
encoding: privateKeyKeyEncoding,
algorithm: privateKeyAlgorithm
} = infisicalSymmetricEncypt(privateKey);
const {
ciphertext: encryptedSymmetricKey,
iv: symmetricKeyIV,
tag: symmetricKeyTag,
encoding: symmetricKeyKeyEncoding,
algorithm: symmetricKeyAlgorithm
} = infisicalSymmetricEncypt(key);
return orgBotDAL.create(
{
name: "Infisical org bot",
publicKey,
privateKeyIV,
encryptedPrivateKey,
symmetricKeyIV,
symmetricKeyTag,
encryptedSymmetricKey,
symmetricKeyAlgorithm,
orgId: identityMembershipOrg.orgId,
privateKeyTag,
privateKeyAlgorithm,
privateKeyKeyEncoding,
symmetricKeyKeyEncoding
},
tx
);
}); });
const key = infisicalSymmetricDecrypt({
ciphertext: orgBot.encryptedSymmetricKey,
iv: orgBot.symmetricKeyIV,
tag: orgBot.symmetricKeyTag,
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
});
const { ciphertext: encryptedCaCert, iv: caCertIV, tag: caCertTag } = encryptSymmetric(caCert, key);
const identityOidcAuth = await identityOidcAuthDAL.transaction(async (tx) => { const identityOidcAuth = await identityOidcAuthDAL.transaction(async (tx) => {
const doc = await identityOidcAuthDAL.create( const doc = await identityOidcAuthDAL.create(
{ {
identityId: identityMembershipOrg.identityId, identityId: identityMembershipOrg.identityId,
oidcDiscoveryUrl, oidcDiscoveryUrl,
encryptedCaCert, encryptedCaCertificate: encryptor({ plainText: Buffer.from(caCert) }).cipherTextBlob,
caCertIV,
caCertTag,
boundIssuer, boundIssuer,
boundAudiences, boundAudiences,
boundClaims, boundClaims,
@@ -415,38 +344,19 @@ export const identityOidcAuthServiceFactory = ({
: undefined : undefined
}; };
const orgBot = await orgBotDAL.findOne({ orgId: identityMembershipOrg.orgId }); const { encryptor, decryptor } = await kmsService.createCipherPairWithDataKey({
if (!orgBot) { type: KmsDataKey.Organization,
throw new NotFoundError({ orgId: identityMembershipOrg.orgId
message: `Organization bot not found for organization with ID '${identityMembershipOrg.orgId}'`,
name: "OrgBotNotFound"
});
}
const key = infisicalSymmetricDecrypt({
ciphertext: orgBot.encryptedSymmetricKey,
iv: orgBot.symmetricKeyIV,
tag: orgBot.symmetricKeyTag,
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
}); });
if (caCert !== undefined) { if (caCert !== undefined) {
const { ciphertext: encryptedCACert, iv: caCertIV, tag: caCertTag } = encryptSymmetric(caCert, key); updateQuery.encryptedCaCertificate = encryptor({ plainText: Buffer.from(caCert) }).cipherTextBlob;
updateQuery.encryptedCaCert = encryptedCACert;
updateQuery.caCertIV = caCertIV;
updateQuery.caCertTag = caCertTag;
} }
const updatedOidcAuth = await identityOidcAuthDAL.updateById(identityOidcAuth.id, updateQuery); const updatedOidcAuth = await identityOidcAuthDAL.updateById(identityOidcAuth.id, updateQuery);
const updatedCACert = const updatedCACert = updatedOidcAuth.encryptedCaCertificate
updatedOidcAuth.encryptedCaCert && updatedOidcAuth.caCertIV && updatedOidcAuth.caCertTag ? decryptor({ cipherTextBlob: updatedOidcAuth.encryptedCaCertificate }).toString()
? decryptSymmetric({ : "";
ciphertext: updatedOidcAuth.encryptedCaCert,
iv: updatedOidcAuth.caCertIV,
tag: updatedOidcAuth.caCertTag,
key
})
: "";
return { return {
...updatedOidcAuth, ...updatedOidcAuth,
@@ -476,27 +386,14 @@ export const identityOidcAuthServiceFactory = ({
const identityOidcAuth = await identityOidcAuthDAL.findOne({ identityId }); const identityOidcAuth = await identityOidcAuthDAL.findOne({ identityId });
const orgBot = await orgBotDAL.findOne({ orgId: identityMembershipOrg.orgId }); const { decryptor } = await kmsService.createCipherPairWithDataKey({
if (!orgBot) { type: KmsDataKey.Organization,
throw new NotFoundError({ orgId: identityMembershipOrg.orgId
message: `Organization bot not found for organization with ID ${identityMembershipOrg.orgId}`,
name: "OrgBotNotFound"
});
}
const key = infisicalSymmetricDecrypt({
ciphertext: orgBot.encryptedSymmetricKey,
iv: orgBot.symmetricKeyIV,
tag: orgBot.symmetricKeyTag,
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
}); });
const caCert = decryptSymmetric({ const caCert = identityOidcAuth.encryptedCaCertificate
ciphertext: identityOidcAuth.encryptedCaCert, ? decryptor({ cipherTextBlob: identityOidcAuth.encryptedCaCertificate }).toString()
iv: identityOidcAuth.caCertIV, : "";
tag: identityOidcAuth.caCertTag,
key
});
return { ...identityOidcAuth, orgId: identityMembershipOrg.orgId, caCert }; return { ...identityOidcAuth, orgId: identityMembershipOrg.orgId, caCert };
}; };
@@ -9,7 +9,7 @@ import {
ProjectPermissionSub ProjectPermissionSub
} from "@app/ee/services/permission/project-permission"; } from "@app/ee/services/permission/project-permission";
import { BadRequestError, NotFoundError } from "@app/lib/errors"; import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { UnpackedPermissionSchema } from "@app/server/routes/santizedSchemas/permission"; import { UnpackedPermissionSchema } from "@app/server/routes/sanitizedSchema/permission";
import { ActorAuthMethod } from "../auth/auth-type"; import { ActorAuthMethod } from "../auth/auth-type";
import { TIdentityProjectMembershipRoleDALFactory } from "../identity-project/identity-project-membership-role-dal"; import { TIdentityProjectMembershipRoleDALFactory } from "../identity-project/identity-project-membership-role-dal";