mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-10 05:28:29 +00:00
Merge branch 'main' into feature/north-flank-app-connection
This commit is contained in:
@@ -49,9 +49,6 @@ RUN rm -fr ${SOFTHSM2_SOURCES}
|
|||||||
# Install pkcs11-tool
|
# Install pkcs11-tool
|
||||||
RUN apt-get install -y opensc
|
RUN apt-get install -y opensc
|
||||||
|
|
||||||
RUN mkdir -p /etc/softhsm2/tokens && \
|
|
||||||
softhsm2-util --init-token --slot 0 --label "auth-app" --pin 1234 --so-pin 0000
|
|
||||||
|
|
||||||
# ? App setup
|
# ? App setup
|
||||||
|
|
||||||
# Install Infisical CLI
|
# Install Infisical CLI
|
||||||
@@ -64,10 +61,14 @@ WORKDIR /app
|
|||||||
COPY package.json package.json
|
COPY package.json package.json
|
||||||
COPY package-lock.json package-lock.json
|
COPY package-lock.json package-lock.json
|
||||||
|
|
||||||
|
COPY dev-entrypoint.sh dev-entrypoint.sh
|
||||||
|
RUN chmod +x dev-entrypoint.sh
|
||||||
|
|
||||||
RUN npm install
|
RUN npm install
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
ENV HOST=0.0.0.0
|
ENV HOST=0.0.0.0
|
||||||
|
|
||||||
|
ENTRYPOINT ["/app/dev-entrypoint.sh"]
|
||||||
CMD ["npm", "run", "dev:docker"]
|
CMD ["npm", "run", "dev:docker"]
|
||||||
|
|||||||
@@ -50,9 +50,6 @@ RUN rm -fr ${SOFTHSM2_SOURCES}
|
|||||||
# Install pkcs11-tool
|
# Install pkcs11-tool
|
||||||
RUN apt-get install -y opensc
|
RUN apt-get install -y opensc
|
||||||
|
|
||||||
RUN mkdir -p /etc/softhsm2/tokens && \
|
|
||||||
softhsm2-util --init-token --slot 0 --label "auth-app" --pin 1234 --so-pin 0000
|
|
||||||
|
|
||||||
WORKDIR /openssl-build
|
WORKDIR /openssl-build
|
||||||
RUN wget https://www.openssl.org/source/openssl-3.1.2.tar.gz \
|
RUN wget https://www.openssl.org/source/openssl-3.1.2.tar.gz \
|
||||||
&& tar -xf openssl-3.1.2.tar.gz \
|
&& tar -xf openssl-3.1.2.tar.gz \
|
||||||
@@ -77,6 +74,9 @@ WORKDIR /app
|
|||||||
COPY package.json package.json
|
COPY package.json package.json
|
||||||
COPY package-lock.json package-lock.json
|
COPY package-lock.json package-lock.json
|
||||||
|
|
||||||
|
COPY dev-entrypoint.sh dev-entrypoint.sh
|
||||||
|
RUN chmod +x dev-entrypoint.sh
|
||||||
|
|
||||||
RUN npm install
|
RUN npm install
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
@@ -87,4 +87,5 @@ ENV OPENSSL_MODULES=/usr/local/lib/ossl-modules
|
|||||||
# ENV NODE_OPTIONS=--force-fips # Note(Daniel): We can't set this on the node options because it may break for existing folks using the infisical/infisical-fips image. Instead we call crypto.setFips(true) at runtime.
|
# ENV NODE_OPTIONS=--force-fips # Note(Daniel): We can't set this on the node options because it may break for existing folks using the infisical/infisical-fips image. Instead we call crypto.setFips(true) at runtime.
|
||||||
ENV FIPS_ENABLED=true
|
ENV FIPS_ENABLED=true
|
||||||
|
|
||||||
|
ENTRYPOINT ["/app/dev-entrypoint.sh"]
|
||||||
CMD ["npm", "run", "dev:docker"]
|
CMD ["npm", "run", "dev:docker"]
|
||||||
|
|||||||
Executable
+16
@@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
update-ca-certificates
|
||||||
|
|
||||||
|
# Initialize SoftHSM token if it doesn't exist
|
||||||
|
if [ ! -f /etc/softhsm2/tokens/auth-app.db ]; then
|
||||||
|
echo "Initializing SoftHSM token..."
|
||||||
|
mkdir -p /etc/softhsm2/tokens
|
||||||
|
softhsm2-util --init-token --slot 0 --label "auth-app" --pin 1234 --so-pin 0000
|
||||||
|
echo "SoftHSM token initialized"
|
||||||
|
else
|
||||||
|
echo "SoftHSM token already exists, skipping initialization"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
exec "$@"
|
||||||
@@ -146,7 +146,8 @@ describe("Service token secret ops", async () => {
|
|||||||
let folderId = "";
|
let folderId = "";
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
initLogger();
|
initLogger();
|
||||||
await initEnvConfig(testSuperAdminDAL, logger);
|
|
||||||
|
await initEnvConfig(testHsmService, testKmsRootConfigDAL, testSuperAdminDAL, logger);
|
||||||
|
|
||||||
serviceToken = await createServiceToken(
|
serviceToken = await createServiceToken(
|
||||||
[{ secretPath: "/**", environment: seedData1.environment.slug }],
|
[{ secretPath: "/**", environment: seedData1.environment.slug }],
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ describe("Secret V3 Router", async () => {
|
|||||||
let folderId = "";
|
let folderId = "";
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
initLogger();
|
initLogger();
|
||||||
await initEnvConfig(testSuperAdminDAL, logger);
|
await initEnvConfig(testHsmService, testKmsRootConfigDAL, testSuperAdminDAL, logger);
|
||||||
|
|
||||||
const projectKeyRes = await testServer.inject({
|
const projectKeyRes = await testServer.inject({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { crypto } from "@app/lib/crypto/cryptography";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
import { seedData1 } from "@app/db/seed-data";
|
import { seedData1 } from "@app/db/seed-data";
|
||||||
import { getDatabaseCredentials, initEnvConfig } from "@app/lib/config/env";
|
import { getDatabaseCredentials, getHsmConfig, initEnvConfig } from "@app/lib/config/env";
|
||||||
import { initLogger } from "@app/lib/logger";
|
import { initLogger } from "@app/lib/logger";
|
||||||
import { main } from "@app/server/app";
|
import { main } from "@app/server/app";
|
||||||
import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type";
|
import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type";
|
||||||
@@ -20,6 +20,8 @@ import { initializeHsmModule } from "@app/ee/services/hsm/hsm-fns";
|
|||||||
import { buildRedisFromConfig } from "@app/lib/config/redis";
|
import { buildRedisFromConfig } from "@app/lib/config/redis";
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
import { bootstrapCheck } from "@app/server/boot-strap-check";
|
import { bootstrapCheck } from "@app/server/boot-strap-check";
|
||||||
|
import { hsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
|
||||||
|
import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
|
|
||||||
dotenv.config({ path: path.join(__dirname, "../../.env.test"), debug: true });
|
dotenv.config({ path: path.join(__dirname, "../../.env.test"), debug: true });
|
||||||
export default {
|
export default {
|
||||||
@@ -28,6 +30,7 @@ export default {
|
|||||||
async setup() {
|
async setup() {
|
||||||
const logger = initLogger();
|
const logger = initLogger();
|
||||||
const databaseCredentials = getDatabaseCredentials(logger);
|
const databaseCredentials = getDatabaseCredentials(logger);
|
||||||
|
const hsmConfig = getHsmConfig(logger);
|
||||||
|
|
||||||
const db = initDbConnection({
|
const db = initDbConnection({
|
||||||
dbConnectionUri: databaseCredentials.dbConnectionUri,
|
dbConnectionUri: databaseCredentials.dbConnectionUri,
|
||||||
@@ -35,7 +38,19 @@ export default {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const superAdminDAL = superAdminDALFactory(db);
|
const superAdminDAL = superAdminDALFactory(db);
|
||||||
const envCfg = await initEnvConfig(superAdminDAL, logger);
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(db);
|
||||||
|
|
||||||
|
const hsmModule = initializeHsmModule(hsmConfig);
|
||||||
|
hsmModule.initialize();
|
||||||
|
|
||||||
|
const hsmService = hsmServiceFactory({
|
||||||
|
hsmModule: hsmModule.getModule(),
|
||||||
|
envConfig: hsmConfig
|
||||||
|
});
|
||||||
|
|
||||||
|
await hsmService.startService();
|
||||||
|
|
||||||
|
const envCfg = await initEnvConfig(hsmService, kmsRootConfigDAL, superAdminDAL, logger);
|
||||||
|
|
||||||
const redis = buildRedisFromConfig(envCfg);
|
const redis = buildRedisFromConfig(envCfg);
|
||||||
await redis.flushdb("SYNC");
|
await redis.flushdb("SYNC");
|
||||||
@@ -68,16 +83,14 @@ export default {
|
|||||||
|
|
||||||
await queue.initialize();
|
await queue.initialize();
|
||||||
|
|
||||||
const hsmModule = initializeHsmModule(envCfg);
|
|
||||||
hsmModule.initialize();
|
|
||||||
|
|
||||||
const server = await main({
|
const server = await main({
|
||||||
db,
|
db,
|
||||||
smtp,
|
smtp,
|
||||||
logger,
|
logger,
|
||||||
queue,
|
queue,
|
||||||
keyStore,
|
keyStore,
|
||||||
hsmModule: hsmModule.getModule(),
|
hsmService,
|
||||||
|
kmsRootConfigDAL,
|
||||||
superAdminDAL,
|
superAdminDAL,
|
||||||
redis,
|
redis,
|
||||||
envConfig: envCfg
|
envConfig: envCfg
|
||||||
@@ -92,6 +105,10 @@ export default {
|
|||||||
// @ts-expect-error type
|
// @ts-expect-error type
|
||||||
globalThis.testSuperAdminDAL = superAdminDAL;
|
globalThis.testSuperAdminDAL = superAdminDAL;
|
||||||
// @ts-expect-error type
|
// @ts-expect-error type
|
||||||
|
globalThis.testKmsRootConfigDAL = kmsRootConfigDAL;
|
||||||
|
// @ts-expect-error type
|
||||||
|
globalThis.testHsmService = hsmService;
|
||||||
|
// @ts-expect-error type
|
||||||
globalThis.jwtAuthToken = crypto.jwt().sign(
|
globalThis.jwtAuthToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
authTokenType: AuthTokenType.ACCESS_TOKEN,
|
authTokenType: AuthTokenType.ACCESS_TOKEN,
|
||||||
|
|||||||
Vendored
+4
@@ -1,7 +1,9 @@
|
|||||||
import { FastifyInstance, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerDefault } from "fastify";
|
import { FastifyInstance, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerDefault } from "fastify";
|
||||||
|
|
||||||
|
import { THsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
|
||||||
import { CustomLogger } from "@app/lib/logger/logger";
|
import { CustomLogger } from "@app/lib/logger/logger";
|
||||||
import { ZodTypeProvider } from "@app/server/plugins/fastify-zod";
|
import { ZodTypeProvider } from "@app/server/plugins/fastify-zod";
|
||||||
|
import { TKmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@@ -16,5 +18,7 @@ declare global {
|
|||||||
// used only for testing
|
// used only for testing
|
||||||
const testServer: FastifyZodProvider;
|
const testServer: FastifyZodProvider;
|
||||||
const testSuperAdminDAL: TSuperAdminDALFactory;
|
const testSuperAdminDAL: TSuperAdminDALFactory;
|
||||||
|
const testKmsRootConfigDAL: TKmsRootConfigDALFactory;
|
||||||
|
const testHsmService: THsmServiceFactory;
|
||||||
const jwtAuthToken: string;
|
const jwtAuthToken: string;
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+4
@@ -48,6 +48,7 @@ import { TSshCertificateAuthorityServiceFactory } from "@app/ee/services/ssh/ssh
|
|||||||
import { TSshCertificateTemplateServiceFactory } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-service";
|
import { TSshCertificateTemplateServiceFactory } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-service";
|
||||||
import { TSshHostServiceFactory } from "@app/ee/services/ssh-host/ssh-host-service";
|
import { TSshHostServiceFactory } from "@app/ee/services/ssh-host/ssh-host-service";
|
||||||
import { TSshHostGroupServiceFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-service";
|
import { TSshHostGroupServiceFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-service";
|
||||||
|
import { TSubOrgServiceFactory } from "@app/ee/services/sub-org/sub-org-service";
|
||||||
import { TTrustedIpServiceFactory } from "@app/ee/services/trusted-ip/trusted-ip-types";
|
import { TTrustedIpServiceFactory } from "@app/ee/services/trusted-ip/trusted-ip-types";
|
||||||
import { TAuthMode } from "@app/server/plugins/auth/inject-identity";
|
import { TAuthMode } from "@app/server/plugins/auth/inject-identity";
|
||||||
import { TAdditionalPrivilegeServiceFactory } from "@app/services/additional-privilege/additional-privilege-service";
|
import { TAdditionalPrivilegeServiceFactory } from "@app/services/additional-privilege/additional-privilege-service";
|
||||||
@@ -182,6 +183,8 @@ declare module "fastify" {
|
|||||||
type: ActorType;
|
type: ActorType;
|
||||||
id: string;
|
id: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
|
parentOrgId: string;
|
||||||
|
rootOrgId: string;
|
||||||
};
|
};
|
||||||
rateLimits: RateLimitConfiguration;
|
rateLimits: RateLimitConfiguration;
|
||||||
// passport data
|
// passport data
|
||||||
@@ -335,6 +338,7 @@ declare module "fastify" {
|
|||||||
additionalPrivilege: TAdditionalPrivilegeServiceFactory;
|
additionalPrivilege: TAdditionalPrivilegeServiceFactory;
|
||||||
role: TRoleServiceFactory;
|
role: TRoleServiceFactory;
|
||||||
convertor: TConvertorServiceFactory;
|
convertor: TConvertorServiceFactory;
|
||||||
|
subOrganization: TSubOrgServiceFactory;
|
||||||
};
|
};
|
||||||
// this is exclusive use for middlewares in which we need to inject data
|
// this is exclusive use for middlewares in which we need to inject data
|
||||||
// everywhere else access using service layer
|
// everywhere else access using service layer
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ import { Knex } from "knex";
|
|||||||
import { inMemoryKeyStore } from "@app/keystore/memory";
|
import { inMemoryKeyStore } from "@app/keystore/memory";
|
||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
import { initLogger } from "@app/lib/logger";
|
import { initLogger } from "@app/lib/logger";
|
||||||
|
import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
import { SecretKeyEncoding, TableName } from "../schemas";
|
import { SecretKeyEncoding, TableName } from "../schemas";
|
||||||
import { getMigrationEnvConfig } from "./utils/env-config";
|
import { getMigrationEnvConfig, getMigrationHsmConfig } from "./utils/env-config";
|
||||||
import { createCircularCache } from "./utils/ring-buffer";
|
import { createCircularCache } from "./utils/ring-buffer";
|
||||||
import { getMigrationEncryptionServices } from "./utils/services";
|
import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services";
|
||||||
|
|
||||||
const BATCH_SIZE = 500;
|
const BATCH_SIZE = 500;
|
||||||
export async function up(knex: Knex): Promise<void> {
|
export async function up(knex: Knex): Promise<void> {
|
||||||
@@ -25,10 +26,12 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
if (hasUrl) t.string("url").nullable().alter();
|
if (hasUrl) t.string("url").nullable().alter();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
initLogger();
|
initLogger();
|
||||||
|
|
||||||
|
const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() });
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
const superAdminDAL = superAdminDALFactory(knex);
|
||||||
const envConfig = await getMigrationEnvConfig(superAdminDAL);
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(knex);
|
||||||
|
const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL);
|
||||||
|
|
||||||
const keyStore = inMemoryKeyStore();
|
const keyStore = inMemoryKeyStore();
|
||||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import { inMemoryKeyStore } from "@app/keystore/memory";
|
|||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
import { selectAllTableCols } from "@app/lib/knex";
|
import { selectAllTableCols } from "@app/lib/knex";
|
||||||
import { initLogger } from "@app/lib/logger";
|
import { initLogger } from "@app/lib/logger";
|
||||||
|
import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
import { SecretKeyEncoding, TableName } from "../schemas";
|
import { SecretKeyEncoding, TableName } from "../schemas";
|
||||||
import { getMigrationEnvConfig } from "./utils/env-config";
|
import { getMigrationEnvConfig, getMigrationHsmConfig } from "./utils/env-config";
|
||||||
import { createCircularCache } from "./utils/ring-buffer";
|
import { createCircularCache } from "./utils/ring-buffer";
|
||||||
import { getMigrationEncryptionServices } from "./utils/services";
|
import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services";
|
||||||
|
|
||||||
const BATCH_SIZE = 500;
|
const BATCH_SIZE = 500;
|
||||||
export async function up(knex: Knex): Promise<void> {
|
export async function up(knex: Knex): Promise<void> {
|
||||||
@@ -30,8 +31,12 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initLogger();
|
initLogger();
|
||||||
|
|
||||||
|
const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() });
|
||||||
|
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
const superAdminDAL = superAdminDALFactory(knex);
|
||||||
const envConfig = await getMigrationEnvConfig(superAdminDAL);
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(knex);
|
||||||
|
const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL);
|
||||||
|
|
||||||
const keyStore = inMemoryKeyStore();
|
const keyStore = inMemoryKeyStore();
|
||||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import { inMemoryKeyStore } from "@app/keystore/memory";
|
|||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
import { selectAllTableCols } from "@app/lib/knex";
|
import { selectAllTableCols } from "@app/lib/knex";
|
||||||
import { initLogger } from "@app/lib/logger";
|
import { initLogger } from "@app/lib/logger";
|
||||||
|
import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
import { SecretKeyEncoding, TableName } from "../schemas";
|
import { SecretKeyEncoding, TableName } from "../schemas";
|
||||||
import { getMigrationEnvConfig } from "./utils/env-config";
|
import { getMigrationEnvConfig, getMigrationHsmConfig } from "./utils/env-config";
|
||||||
import { createCircularCache } from "./utils/ring-buffer";
|
import { createCircularCache } from "./utils/ring-buffer";
|
||||||
import { getMigrationEncryptionServices } from "./utils/services";
|
import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services";
|
||||||
|
|
||||||
const BATCH_SIZE = 500;
|
const BATCH_SIZE = 500;
|
||||||
export async function up(knex: Knex): Promise<void> {
|
export async function up(knex: Knex): Promise<void> {
|
||||||
@@ -24,8 +25,11 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initLogger();
|
initLogger();
|
||||||
|
const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() });
|
||||||
|
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
const superAdminDAL = superAdminDALFactory(knex);
|
||||||
const envConfig = await getMigrationEnvConfig(superAdminDAL);
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(knex);
|
||||||
|
const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL);
|
||||||
|
|
||||||
const keyStore = inMemoryKeyStore();
|
const keyStore = inMemoryKeyStore();
|
||||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import { inMemoryKeyStore } from "@app/keystore/memory";
|
|||||||
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
|
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
|
||||||
import { selectAllTableCols } from "@app/lib/knex";
|
import { selectAllTableCols } from "@app/lib/knex";
|
||||||
import { initLogger } from "@app/lib/logger";
|
import { initLogger } from "@app/lib/logger";
|
||||||
|
import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
import { SecretKeyEncoding, TableName, TOrgBots } from "../schemas";
|
import { SecretKeyEncoding, TableName, TOrgBots } from "../schemas";
|
||||||
import { getMigrationEnvConfig } from "./utils/env-config";
|
import { getMigrationEnvConfig, getMigrationHsmConfig } from "./utils/env-config";
|
||||||
import { createCircularCache } from "./utils/ring-buffer";
|
import { createCircularCache } from "./utils/ring-buffer";
|
||||||
import { getMigrationEncryptionServices } from "./utils/services";
|
import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services";
|
||||||
|
|
||||||
const BATCH_SIZE = 500;
|
const BATCH_SIZE = 500;
|
||||||
const reencryptIdentityK8sAuth = async (knex: Knex) => {
|
const reencryptIdentityK8sAuth = async (knex: Knex) => {
|
||||||
@@ -55,9 +56,11 @@ const reencryptIdentityK8sAuth = async (knex: Knex) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initLogger();
|
initLogger();
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() });
|
||||||
const envConfig = await getMigrationEnvConfig(superAdminDAL);
|
|
||||||
|
|
||||||
|
const superAdminDAL = superAdminDALFactory(knex);
|
||||||
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(knex);
|
||||||
|
const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL);
|
||||||
const keyStore = inMemoryKeyStore();
|
const keyStore = inMemoryKeyStore();
|
||||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
||||||
const orgEncryptionRingBuffer =
|
const orgEncryptionRingBuffer =
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import { inMemoryKeyStore } from "@app/keystore/memory";
|
|||||||
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
|
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
|
||||||
import { selectAllTableCols } from "@app/lib/knex";
|
import { selectAllTableCols } from "@app/lib/knex";
|
||||||
import { initLogger } from "@app/lib/logger";
|
import { initLogger } from "@app/lib/logger";
|
||||||
|
import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
import { SecretKeyEncoding, TableName, TOrgBots } from "../schemas";
|
import { SecretKeyEncoding, TableName, TOrgBots } from "../schemas";
|
||||||
import { getMigrationEnvConfig } from "./utils/env-config";
|
import { getMigrationEnvConfig, getMigrationHsmConfig } from "./utils/env-config";
|
||||||
import { createCircularCache } from "./utils/ring-buffer";
|
import { createCircularCache } from "./utils/ring-buffer";
|
||||||
import { getMigrationEncryptionServices } from "./utils/services";
|
import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services";
|
||||||
|
|
||||||
const BATCH_SIZE = 500;
|
const BATCH_SIZE = 500;
|
||||||
const reencryptIdentityOidcAuth = async (knex: Knex) => {
|
const reencryptIdentityOidcAuth = async (knex: Knex) => {
|
||||||
@@ -35,8 +36,11 @@ const reencryptIdentityOidcAuth = async (knex: Knex) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initLogger();
|
initLogger();
|
||||||
|
const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() });
|
||||||
|
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
const superAdminDAL = superAdminDALFactory(knex);
|
||||||
const envConfig = await getMigrationEnvConfig(superAdminDAL);
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(knex);
|
||||||
|
const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL);
|
||||||
|
|
||||||
const keyStore = inMemoryKeyStore();
|
const keyStore = inMemoryKeyStore();
|
||||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
||||||
|
|||||||
@@ -4,16 +4,18 @@ import { inMemoryKeyStore } from "@app/keystore/memory";
|
|||||||
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
|
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
|
||||||
import { selectAllTableCols } from "@app/lib/knex";
|
import { selectAllTableCols } from "@app/lib/knex";
|
||||||
import { initLogger } from "@app/lib/logger";
|
import { initLogger } from "@app/lib/logger";
|
||||||
|
import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
import { SecretKeyEncoding, TableName } from "../schemas";
|
import { SecretKeyEncoding, TableName } from "../schemas";
|
||||||
import { getMigrationEnvConfig } from "./utils/env-config";
|
import { getMigrationEnvConfig, getMigrationHsmConfig } from "./utils/env-config";
|
||||||
import { createCircularCache } from "./utils/ring-buffer";
|
import { createCircularCache } from "./utils/ring-buffer";
|
||||||
import { getMigrationEncryptionServices } from "./utils/services";
|
import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services";
|
||||||
|
|
||||||
const BATCH_SIZE = 500;
|
const BATCH_SIZE = 500;
|
||||||
const reencryptSamlConfig = async (knex: Knex) => {
|
const reencryptSamlConfig = async (knex: Knex, kmsService: TKmsServiceFactory) => {
|
||||||
const hasEncryptedEntrypointColumn = await knex.schema.hasColumn(TableName.SamlConfig, "encryptedSamlEntryPoint");
|
const hasEncryptedEntrypointColumn = await knex.schema.hasColumn(TableName.SamlConfig, "encryptedSamlEntryPoint");
|
||||||
const hasEncryptedIssuerColumn = await knex.schema.hasColumn(TableName.SamlConfig, "encryptedSamlIssuer");
|
const hasEncryptedIssuerColumn = await knex.schema.hasColumn(TableName.SamlConfig, "encryptedSamlIssuer");
|
||||||
const hasEncryptedCertificateColumn = await knex.schema.hasColumn(TableName.SamlConfig, "encryptedSamlCertificate");
|
const hasEncryptedCertificateColumn = await knex.schema.hasColumn(TableName.SamlConfig, "encryptedSamlCertificate");
|
||||||
@@ -28,10 +30,6 @@ const reencryptSamlConfig = async (knex: Knex) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initLogger();
|
initLogger();
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
|
||||||
const envConfig = await getMigrationEnvConfig(superAdminDAL);
|
|
||||||
const keyStore = inMemoryKeyStore();
|
|
||||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
|
||||||
const orgEncryptionRingBuffer =
|
const orgEncryptionRingBuffer =
|
||||||
createCircularCache<Awaited<ReturnType<(typeof kmsService)["createCipherPairWithDataKey"]>>>(25);
|
createCircularCache<Awaited<ReturnType<(typeof kmsService)["createCipherPairWithDataKey"]>>>(25);
|
||||||
|
|
||||||
@@ -159,7 +157,7 @@ const reencryptSamlConfig = async (knex: Knex) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const reencryptLdapConfig = async (knex: Knex) => {
|
const reencryptLdapConfig = async (knex: Knex, kmsService: TKmsServiceFactory) => {
|
||||||
const hasEncryptedLdapBindDNColum = await knex.schema.hasColumn(TableName.LdapConfig, "encryptedLdapBindDN");
|
const hasEncryptedLdapBindDNColum = await knex.schema.hasColumn(TableName.LdapConfig, "encryptedLdapBindDN");
|
||||||
const hasEncryptedLdapBindPassColumn = await knex.schema.hasColumn(TableName.LdapConfig, "encryptedLdapBindPass");
|
const hasEncryptedLdapBindPassColumn = await knex.schema.hasColumn(TableName.LdapConfig, "encryptedLdapBindPass");
|
||||||
const hasEncryptedCertificateColumn = await knex.schema.hasColumn(TableName.LdapConfig, "encryptedLdapCaCertificate");
|
const hasEncryptedCertificateColumn = await knex.schema.hasColumn(TableName.LdapConfig, "encryptedLdapCaCertificate");
|
||||||
@@ -194,10 +192,6 @@ const reencryptLdapConfig = async (knex: Knex) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initLogger();
|
initLogger();
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
|
||||||
const envConfig = await getMigrationEnvConfig(superAdminDAL);
|
|
||||||
const keyStore = inMemoryKeyStore();
|
|
||||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
|
||||||
const orgEncryptionRingBuffer =
|
const orgEncryptionRingBuffer =
|
||||||
createCircularCache<Awaited<ReturnType<(typeof kmsService)["createCipherPairWithDataKey"]>>>(25);
|
createCircularCache<Awaited<ReturnType<(typeof kmsService)["createCipherPairWithDataKey"]>>>(25);
|
||||||
|
|
||||||
@@ -323,7 +317,7 @@ const reencryptLdapConfig = async (knex: Knex) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const reencryptOidcConfig = async (knex: Knex) => {
|
const reencryptOidcConfig = async (knex: Knex, kmsService: TKmsServiceFactory) => {
|
||||||
const hasEncryptedOidcClientIdColumn = await knex.schema.hasColumn(TableName.OidcConfig, "encryptedOidcClientId");
|
const hasEncryptedOidcClientIdColumn = await knex.schema.hasColumn(TableName.OidcConfig, "encryptedOidcClientId");
|
||||||
const hasEncryptedOidcClientSecretColumn = await knex.schema.hasColumn(
|
const hasEncryptedOidcClientSecretColumn = await knex.schema.hasColumn(
|
||||||
TableName.OidcConfig,
|
TableName.OidcConfig,
|
||||||
@@ -354,10 +348,6 @@ const reencryptOidcConfig = async (knex: Knex) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initLogger();
|
initLogger();
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
|
||||||
const envConfig = await getMigrationEnvConfig(superAdminDAL);
|
|
||||||
const keyStore = inMemoryKeyStore();
|
|
||||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
|
||||||
const orgEncryptionRingBuffer =
|
const orgEncryptionRingBuffer =
|
||||||
createCircularCache<Awaited<ReturnType<(typeof kmsService)["createCipherPairWithDataKey"]>>>(25);
|
createCircularCache<Awaited<ReturnType<(typeof kmsService)["createCipherPairWithDataKey"]>>>(25);
|
||||||
|
|
||||||
@@ -462,9 +452,18 @@ const reencryptOidcConfig = async (knex: Knex) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
export async function up(knex: Knex): Promise<void> {
|
||||||
await reencryptSamlConfig(knex);
|
initLogger();
|
||||||
await reencryptLdapConfig(knex);
|
|
||||||
await reencryptOidcConfig(knex);
|
const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() });
|
||||||
|
const superAdminDAL = superAdminDALFactory(knex);
|
||||||
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(knex);
|
||||||
|
const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL);
|
||||||
|
const keyStore = inMemoryKeyStore();
|
||||||
|
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
||||||
|
|
||||||
|
await reencryptSamlConfig(knex, kmsService);
|
||||||
|
await reencryptLdapConfig(knex, kmsService);
|
||||||
|
await reencryptOidcConfig(knex, kmsService);
|
||||||
}
|
}
|
||||||
|
|
||||||
const dropSamlConfigColumns = async (knex: Knex) => {
|
const dropSamlConfigColumns = async (knex: Knex) => {
|
||||||
|
|||||||
@@ -3,12 +3,13 @@ import { Knex } from "knex";
|
|||||||
import { inMemoryKeyStore } from "@app/keystore/memory";
|
import { inMemoryKeyStore } from "@app/keystore/memory";
|
||||||
import { selectAllTableCols } from "@app/lib/knex";
|
import { selectAllTableCols } from "@app/lib/knex";
|
||||||
import { initLogger } from "@app/lib/logger";
|
import { initLogger } from "@app/lib/logger";
|
||||||
|
import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
import { TableName } from "../schemas";
|
||||||
import { getMigrationEnvConfig } from "./utils/env-config";
|
import { getMigrationEnvConfig, getMigrationHsmConfig } from "./utils/env-config";
|
||||||
import { getMigrationEncryptionServices } from "./utils/services";
|
import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services";
|
||||||
|
|
||||||
// Note(daniel): We aren't dropping tables or columns in this migrations so we can easily rollback if needed.
|
// Note(daniel): We aren't dropping tables or columns in this migrations so we can easily rollback if needed.
|
||||||
// In the future we need to drop the projectGatewayId on the dynamic secrets table, and drop the project_gateways table entirely.
|
// In the future we need to drop the projectGatewayId on the dynamic secrets table, and drop the project_gateways table entirely.
|
||||||
@@ -40,8 +41,10 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
initLogger();
|
initLogger();
|
||||||
|
const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() });
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
const superAdminDAL = superAdminDALFactory(knex);
|
||||||
const envConfig = await getMigrationEnvConfig(superAdminDAL);
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(knex);
|
||||||
|
const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL);
|
||||||
const keyStore = inMemoryKeyStore();
|
const keyStore = inMemoryKeyStore();
|
||||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
||||||
|
|
||||||
|
|||||||
@@ -2,19 +2,23 @@ import { Knex } from "knex";
|
|||||||
|
|
||||||
import { inMemoryKeyStore } from "@app/keystore/memory";
|
import { inMemoryKeyStore } from "@app/keystore/memory";
|
||||||
import { selectAllTableCols } from "@app/lib/knex";
|
import { selectAllTableCols } from "@app/lib/knex";
|
||||||
|
import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
import { TableName } from "../schemas";
|
||||||
import { getMigrationEnvConfig } from "./utils/env-config";
|
import { getMigrationEnvConfig, getMigrationHsmConfig } from "./utils/env-config";
|
||||||
import { getMigrationEncryptionServices } from "./utils/services";
|
import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services";
|
||||||
|
|
||||||
export async function up(knex: Knex) {
|
export async function up(knex: Knex) {
|
||||||
const existingSuperAdminsWithGithubConnection = await knex(TableName.SuperAdmin)
|
const existingSuperAdminsWithGithubConnection = await knex(TableName.SuperAdmin)
|
||||||
.select(selectAllTableCols(TableName.SuperAdmin))
|
.select(selectAllTableCols(TableName.SuperAdmin))
|
||||||
.whereNotNull(`${TableName.SuperAdmin}.encryptedGitHubAppConnectionClientId`);
|
.whereNotNull(`${TableName.SuperAdmin}.encryptedGitHubAppConnectionClientId`);
|
||||||
|
|
||||||
|
const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() });
|
||||||
|
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
const superAdminDAL = superAdminDALFactory(knex);
|
||||||
const envConfig = await getMigrationEnvConfig(superAdminDAL);
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(knex);
|
||||||
|
const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL);
|
||||||
const keyStore = inMemoryKeyStore();
|
const keyStore = inMemoryKeyStore();
|
||||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,14 @@ import { Knex } from "knex";
|
|||||||
|
|
||||||
import { inMemoryKeyStore } from "@app/keystore/memory";
|
import { inMemoryKeyStore } from "@app/keystore/memory";
|
||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
|
import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
import { SecretKeyEncoding, TableName } from "../schemas";
|
import { SecretKeyEncoding, TableName } from "../schemas";
|
||||||
import { getMigrationEnvConfig } from "./utils/env-config";
|
import { getMigrationEnvConfig, getMigrationHsmConfig } from "./utils/env-config";
|
||||||
import { createCircularCache } from "./utils/ring-buffer";
|
import { createCircularCache } from "./utils/ring-buffer";
|
||||||
import { getMigrationEncryptionServices } from "./utils/services";
|
import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services";
|
||||||
|
|
||||||
const BATCH_SIZE = 500;
|
const BATCH_SIZE = 500;
|
||||||
export async function up(knex: Knex): Promise<void> {
|
export async function up(knex: Knex): Promise<void> {
|
||||||
@@ -25,8 +26,10 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!hasEncryptedCredentials) {
|
if (!hasEncryptedCredentials) {
|
||||||
|
const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() });
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
const superAdminDAL = superAdminDALFactory(knex);
|
||||||
const envConfig = await getMigrationEnvConfig(superAdminDAL);
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(knex);
|
||||||
|
const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL);
|
||||||
const keyStore = inMemoryKeyStore();
|
const keyStore = inMemoryKeyStore();
|
||||||
|
|
||||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
||||||
@@ -131,8 +134,11 @@ export async function down(knex: Knex): Promise<void> {
|
|||||||
const hasEncryptedCredentials = await knex.schema.hasColumn(TableName.AuditLogStream, "encryptedCredentials");
|
const hasEncryptedCredentials = await knex.schema.hasColumn(TableName.AuditLogStream, "encryptedCredentials");
|
||||||
|
|
||||||
if (hasEncryptedCredentials) {
|
if (hasEncryptedCredentials) {
|
||||||
|
const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() });
|
||||||
|
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
const superAdminDAL = superAdminDALFactory(knex);
|
||||||
const envConfig = await getMigrationEnvConfig(superAdminDAL);
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(knex);
|
||||||
|
const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL);
|
||||||
const keyStore = inMemoryKeyStore();
|
const keyStore = inMemoryKeyStore();
|
||||||
|
|
||||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.PamAccount, "rotationEnabled"))) {
|
||||||
|
await knex.schema.alterTable(TableName.PamAccount, (t) => {
|
||||||
|
t.boolean("rotationEnabled").notNullable().defaultTo(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.PamAccount, "rotationIntervalSeconds"))) {
|
||||||
|
await knex.schema.alterTable(TableName.PamAccount, (t) => {
|
||||||
|
t.integer("rotationIntervalSeconds").nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.PamAccount, "lastRotatedAt"))) {
|
||||||
|
await knex.schema.alterTable(TableName.PamAccount, (t) => {
|
||||||
|
t.timestamp("lastRotatedAt").nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.PamResource, "encryptedRotationAccountCredentials"))) {
|
||||||
|
await knex.schema.alterTable(TableName.PamResource, (t) => {
|
||||||
|
t.binary("encryptedRotationAccountCredentials").nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasColumn(TableName.PamResource, "encryptedRotationAccountCredentials")) {
|
||||||
|
await knex.schema.alterTable(TableName.PamResource, (t) => {
|
||||||
|
t.dropColumn("encryptedRotationAccountCredentials");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (await knex.schema.hasColumn(TableName.PamAccount, "rotationEnabled")) {
|
||||||
|
await knex.schema.alterTable(TableName.PamAccount, (t) => {
|
||||||
|
t.dropColumn("rotationEnabled");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (await knex.schema.hasColumn(TableName.PamAccount, "rotationIntervalSeconds")) {
|
||||||
|
await knex.schema.alterTable(TableName.PamAccount, (t) => {
|
||||||
|
t.dropColumn("rotationIntervalSeconds");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (await knex.schema.hasColumn(TableName.PamAccount, "lastRotatedAt")) {
|
||||||
|
await knex.schema.alterTable(TableName.PamAccount, (t) => {
|
||||||
|
t.dropColumn("lastRotatedAt");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { dropConstraintIfExists } from "@app/db/migrations/utils/dropConstraintIfExists";
|
||||||
|
|
||||||
|
import { AccessScope, TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const hasParentOrgId = await knex.schema.hasColumn(TableName.Organization, "parentOrgId");
|
||||||
|
if (!hasParentOrgId) {
|
||||||
|
await knex.schema.alterTable(TableName.Organization, async (t) => {
|
||||||
|
// the one just above the chain
|
||||||
|
t.uuid("parentOrgId");
|
||||||
|
t.foreign("parentOrgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
|
||||||
|
// this would root organization containing various informations like billing etc
|
||||||
|
t.uuid("rootOrgId");
|
||||||
|
t.foreign("rootOrgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
|
||||||
|
|
||||||
|
await dropConstraintIfExists(TableName.Organization, "organizations_slug_unique", knex);
|
||||||
|
t.unique(["rootOrgId", "parentOrgId", "slug"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// had to switch to raw for null not distinct
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasIdentityOrgCol = await knex.schema.hasColumn(TableName.Identity, "orgId");
|
||||||
|
if (!hasIdentityOrgCol) {
|
||||||
|
await knex.schema.alterTable(TableName.Identity, (t) => {
|
||||||
|
t.uuid("orgId");
|
||||||
|
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.raw(
|
||||||
|
`
|
||||||
|
UPDATE ?? AS identity
|
||||||
|
SET "orgId" = membership."scopeOrgId"
|
||||||
|
FROM ?? AS membership
|
||||||
|
WHERE
|
||||||
|
membership."actorIdentityId" = identity."id"
|
||||||
|
AND membership."scope" = ?
|
||||||
|
`,
|
||||||
|
[TableName.Identity, TableName.Membership, AccessScope.Organization]
|
||||||
|
);
|
||||||
|
|
||||||
|
await knex.raw(`DELETE FROM ?? WHERE "orgId" IS NULL`, [TableName.Identity]);
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.Identity, (t) => {
|
||||||
|
t.uuid("orgId").notNullable().alter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const hasParentOrgId = await knex.schema.hasColumn(TableName.Organization, "parentOrgId");
|
||||||
|
const hasRootOrgId = await knex.schema.hasColumn(TableName.Organization, "rootOrgId");
|
||||||
|
if (hasParentOrgId || hasRootOrgId) {
|
||||||
|
await knex.schema.alterTable(TableName.Organization, (t) => {
|
||||||
|
if (hasParentOrgId) t.dropColumn("parentOrgId");
|
||||||
|
if (hasRootOrgId) t.dropColumn("rootOrgId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasIdentityOrgCol = await knex.schema.hasColumn(TableName.Identity, "orgId");
|
||||||
|
if (hasIdentityOrgCol) {
|
||||||
|
await knex.schema.alterTable(TableName.Identity, (t) => {
|
||||||
|
t.dropColumn("orgId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { THsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
|
||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
|
import { removeTrailingSlash } from "@app/lib/fn";
|
||||||
import { zpStr } from "@app/lib/zod";
|
import { zpStr } from "@app/lib/zod";
|
||||||
|
import { TKmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
const envSchema = z
|
const envSchema = z
|
||||||
@@ -22,13 +25,17 @@ const envSchema = z
|
|||||||
HSM_LIB_PATH: zpStr(z.string().optional()),
|
HSM_LIB_PATH: zpStr(z.string().optional()),
|
||||||
HSM_PIN: zpStr(z.string().optional()),
|
HSM_PIN: zpStr(z.string().optional()),
|
||||||
HSM_KEY_LABEL: zpStr(z.string().optional()),
|
HSM_KEY_LABEL: zpStr(z.string().optional()),
|
||||||
HSM_SLOT: z.coerce.number().optional().default(0)
|
HSM_SLOT: z.coerce.number().optional().default(0),
|
||||||
|
|
||||||
|
LICENSE_SERVER_URL: zpStr(z.string().optional().default("https://portal.infisical.com")),
|
||||||
|
LICENSE_SERVER_KEY: zpStr(z.string().optional()),
|
||||||
|
LICENSE_KEY: zpStr(z.string().optional()),
|
||||||
|
LICENSE_KEY_OFFLINE: zpStr(z.string().optional()),
|
||||||
|
INTERNAL_REGION: zpStr(z.enum(["us", "eu"]).optional()),
|
||||||
|
|
||||||
|
SITE_URL: zpStr(z.string().transform((val) => (val ? removeTrailingSlash(val) : val))).optional()
|
||||||
})
|
})
|
||||||
// To ensure that basic encryption is always possible.
|
// To ensure that basic encryption is always possible.
|
||||||
.refine(
|
|
||||||
(data) => Boolean(data.ENCRYPTION_KEY) || Boolean(data.ROOT_ENCRYPTION_KEY),
|
|
||||||
"Either ENCRYPTION_KEY or ROOT_ENCRYPTION_KEY must be defined."
|
|
||||||
)
|
|
||||||
.transform((data) => ({
|
.transform((data) => ({
|
||||||
...data,
|
...data,
|
||||||
isHsmConfigured:
|
isHsmConfigured:
|
||||||
@@ -37,7 +44,27 @@ const envSchema = z
|
|||||||
|
|
||||||
export type TMigrationEnvConfig = z.infer<typeof envSchema>;
|
export type TMigrationEnvConfig = z.infer<typeof envSchema>;
|
||||||
|
|
||||||
export const getMigrationEnvConfig = async (superAdminDAL: TSuperAdminDALFactory) => {
|
export const getMigrationHsmConfig = () => {
|
||||||
|
const parsedEnv = envSchema.safeParse(process.env);
|
||||||
|
if (!parsedEnv.success) {
|
||||||
|
console.error("Invalid environment variables. Check the error below");
|
||||||
|
console.error(parsedEnv.error.issues);
|
||||||
|
process.exit(-1);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
isHsmConfigured: parsedEnv.data.isHsmConfigured,
|
||||||
|
HSM_PIN: parsedEnv.data.HSM_PIN,
|
||||||
|
HSM_SLOT: parsedEnv.data.HSM_SLOT,
|
||||||
|
HSM_LIB_PATH: parsedEnv.data.HSM_LIB_PATH,
|
||||||
|
HSM_KEY_LABEL: parsedEnv.data.HSM_KEY_LABEL
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getMigrationEnvConfig = async (
|
||||||
|
superAdminDAL: TSuperAdminDALFactory,
|
||||||
|
hsmService: THsmServiceFactory,
|
||||||
|
kmsRootConfigDAL: TKmsRootConfigDALFactory
|
||||||
|
) => {
|
||||||
const parsedEnv = envSchema.safeParse(process.env);
|
const parsedEnv = envSchema.safeParse(process.env);
|
||||||
if (!parsedEnv.success) {
|
if (!parsedEnv.success) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
@@ -53,7 +80,7 @@ export const getMigrationEnvConfig = async (superAdminDAL: TSuperAdminDALFactory
|
|||||||
|
|
||||||
let envCfg = Object.freeze(parsedEnv.data);
|
let envCfg = Object.freeze(parsedEnv.data);
|
||||||
|
|
||||||
const fipsEnabled = await crypto.initialize(superAdminDAL, envCfg);
|
const fipsEnabled = await crypto.initialize(superAdminDAL, hsmService, kmsRootConfigDAL, envCfg);
|
||||||
|
|
||||||
// Fix for 128-bit entropy encryption key expansion issue:
|
// Fix for 128-bit entropy encryption key expansion issue:
|
||||||
// In FIPS it is not ideal to expand a 128-bit key into 256-bit. We solved this issue in the past by creating the ROOT_ENCRYPTION_KEY.
|
// In FIPS it is not ideal to expand a 128-bit key into 256-bit. We solved this issue in the past by creating the ROOT_ENCRYPTION_KEY.
|
||||||
|
|||||||
@@ -1,28 +1,23 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { initializeHsmModule } from "@app/ee/services/hsm/hsm-fns";
|
import { initializeHsmModule, isHsmActiveAndEnabled } from "@app/ee/services/hsm/hsm-fns";
|
||||||
import { hsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
|
import { hsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
|
||||||
|
import { licenseDALFactory } from "@app/ee/services/license/license-dal";
|
||||||
|
import { licenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
|
import { permissionDALFactory } from "@app/ee/services/permission/permission-dal";
|
||||||
|
import { permissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||||
import { folderCheckpointDALFactory } from "@app/services/folder-checkpoint/folder-checkpoint-dal";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { folderCheckpointResourcesDALFactory } from "@app/services/folder-checkpoint-resources/folder-checkpoint-resources-dal";
|
|
||||||
import { folderCommitDALFactory } from "@app/services/folder-commit/folder-commit-dal";
|
|
||||||
import { folderCommitServiceFactory } from "@app/services/folder-commit/folder-commit-service";
|
|
||||||
import { folderCommitChangesDALFactory } from "@app/services/folder-commit-changes/folder-commit-changes-dal";
|
|
||||||
import { folderTreeCheckpointDALFactory } from "@app/services/folder-tree-checkpoint/folder-tree-checkpoint-dal";
|
|
||||||
import { folderTreeCheckpointResourcesDALFactory } from "@app/services/folder-tree-checkpoint-resources/folder-tree-checkpoint-resources-dal";
|
|
||||||
import { identityDALFactory } from "@app/services/identity/identity-dal";
|
import { identityDALFactory } from "@app/services/identity/identity-dal";
|
||||||
import { internalKmsDALFactory } from "@app/services/kms/internal-kms-dal";
|
import { internalKmsDALFactory } from "@app/services/kms/internal-kms-dal";
|
||||||
import { kmskeyDALFactory } from "@app/services/kms/kms-key-dal";
|
import { kmskeyDALFactory } from "@app/services/kms/kms-key-dal";
|
||||||
import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
import { kmsServiceFactory } from "@app/services/kms/kms-service";
|
import { kmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
|
import { RootKeyEncryptionStrategy } from "@app/services/kms/kms-types";
|
||||||
import { orgDALFactory } from "@app/services/org/org-dal";
|
import { orgDALFactory } from "@app/services/org/org-dal";
|
||||||
import { projectDALFactory } from "@app/services/project/project-dal";
|
import { projectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { resourceMetadataDALFactory } from "@app/services/resource-metadata/resource-metadata-dal";
|
import { roleDALFactory } from "@app/services/role/role-dal";
|
||||||
import { secretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal";
|
import { serviceTokenDALFactory } from "@app/services/service-token/service-token-dal";
|
||||||
import { secretFolderVersionDALFactory } from "@app/services/secret-folder/secret-folder-version-dal";
|
|
||||||
import { secretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal";
|
|
||||||
import { secretV2BridgeDALFactory } from "@app/services/secret-v2-bridge/secret-v2-bridge-dal";
|
|
||||||
import { secretVersionV2BridgeDALFactory } from "@app/services/secret-v2-bridge/secret-version-dal";
|
|
||||||
import { userDALFactory } from "@app/services/user/user-dal";
|
import { userDALFactory } from "@app/services/user/user-dal";
|
||||||
|
|
||||||
import { TMigrationEnvConfig } from "./env-config";
|
import { TMigrationEnvConfig } from "./env-config";
|
||||||
@@ -33,8 +28,11 @@ type TDependencies = {
|
|||||||
keyStore: TKeyStoreFactory;
|
keyStore: TKeyStoreFactory;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getMigrationEncryptionServices = async ({ envConfig, db, keyStore }: TDependencies) => {
|
type THsmServiceDependencies = {
|
||||||
// eslint-disable-next-line no-param-reassign
|
envConfig: Pick<TMigrationEnvConfig, "HSM_PIN" | "HSM_SLOT" | "HSM_LIB_PATH" | "HSM_KEY_LABEL" | "isHsmConfigured">;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getMigrationHsmService = async ({ envConfig }: THsmServiceDependencies) => {
|
||||||
const hsmModule = initializeHsmModule(envConfig);
|
const hsmModule = initializeHsmModule(envConfig);
|
||||||
hsmModule.initialize();
|
hsmModule.initialize();
|
||||||
|
|
||||||
@@ -43,67 +41,72 @@ export const getMigrationEncryptionServices = async ({ envConfig, db, keyStore }
|
|||||||
envConfig
|
envConfig
|
||||||
});
|
});
|
||||||
|
|
||||||
const orgDAL = orgDALFactory(db);
|
|
||||||
const kmsRootConfigDAL = kmsRootConfigDALFactory(db);
|
|
||||||
const kmsDAL = kmskeyDALFactory(db);
|
|
||||||
const internalKmsDAL = internalKmsDALFactory(db);
|
|
||||||
const projectDAL = projectDALFactory(db);
|
|
||||||
|
|
||||||
const kmsService = kmsServiceFactory({
|
|
||||||
kmsRootConfigDAL,
|
|
||||||
keyStore,
|
|
||||||
kmsDAL,
|
|
||||||
internalKmsDAL,
|
|
||||||
orgDAL,
|
|
||||||
projectDAL,
|
|
||||||
hsmService,
|
|
||||||
envConfig
|
|
||||||
});
|
|
||||||
|
|
||||||
await hsmService.startService();
|
await hsmService.startService();
|
||||||
await kmsService.startService();
|
|
||||||
|
|
||||||
return { kmsService };
|
return { hsmService };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getMigrationPITServices = async ({
|
export const getMigrationEncryptionServices = async ({ envConfig, db, keyStore }: TDependencies) => {
|
||||||
db,
|
// ----- DAL dependencies -----
|
||||||
keyStore,
|
const orgDAL = orgDALFactory(db);
|
||||||
envConfig
|
const licenseDAL = licenseDALFactory(db);
|
||||||
}: {
|
const permissionDAL = permissionDALFactory(db);
|
||||||
db: Knex;
|
|
||||||
keyStore: TKeyStoreFactory;
|
|
||||||
envConfig: TMigrationEnvConfig;
|
|
||||||
}) => {
|
|
||||||
const projectDAL = projectDALFactory(db);
|
const projectDAL = projectDALFactory(db);
|
||||||
const folderCommitDAL = folderCommitDALFactory(db);
|
const roleDAL = roleDALFactory(db);
|
||||||
const folderCommitChangesDAL = folderCommitChangesDALFactory(db);
|
|
||||||
const folderCheckpointDAL = folderCheckpointDALFactory(db);
|
|
||||||
const folderTreeCheckpointDAL = folderTreeCheckpointDALFactory(db);
|
|
||||||
const userDAL = userDALFactory(db);
|
const userDAL = userDALFactory(db);
|
||||||
const identityDAL = identityDALFactory(db);
|
const identityDAL = identityDALFactory(db);
|
||||||
const folderDAL = secretFolderDALFactory(db);
|
const serviceTokenDAL = serviceTokenDALFactory(db);
|
||||||
const folderVersionDAL = secretFolderVersionDALFactory(db);
|
|
||||||
const secretVersionV2BridgeDAL = secretVersionV2BridgeDALFactory(db);
|
|
||||||
const folderCheckpointResourcesDAL = folderCheckpointResourcesDALFactory(db);
|
|
||||||
const secretV2BridgeDAL = secretV2BridgeDALFactory({ db, keyStore });
|
|
||||||
const folderTreeCheckpointResourcesDAL = folderTreeCheckpointResourcesDALFactory(db);
|
|
||||||
const secretTagDAL = secretTagDALFactory(db);
|
|
||||||
|
|
||||||
const orgDAL = orgDALFactory(db);
|
|
||||||
const kmsRootConfigDAL = kmsRootConfigDALFactory(db);
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(db);
|
||||||
const kmsDAL = kmskeyDALFactory(db);
|
const kmsDAL = kmskeyDALFactory(db);
|
||||||
const internalKmsDAL = internalKmsDALFactory(db);
|
const internalKmsDAL = internalKmsDALFactory(db);
|
||||||
const resourceMetadataDAL = resourceMetadataDALFactory(db);
|
|
||||||
|
|
||||||
const hsmModule = initializeHsmModule(envConfig);
|
// ----- Service dependencies -----
|
||||||
hsmModule.initialize();
|
const permissionService = permissionServiceFactory({
|
||||||
|
permissionDAL,
|
||||||
|
serviceTokenDAL,
|
||||||
|
projectDAL,
|
||||||
|
keyStore,
|
||||||
|
roleDAL,
|
||||||
|
userDAL,
|
||||||
|
identityDAL
|
||||||
|
});
|
||||||
|
|
||||||
const hsmService = hsmServiceFactory({
|
const licenseService = licenseServiceFactory({
|
||||||
hsmModule: hsmModule.getModule(),
|
permissionService,
|
||||||
|
orgDAL,
|
||||||
|
licenseDAL,
|
||||||
|
keyStore,
|
||||||
|
projectDAL,
|
||||||
envConfig
|
envConfig
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ----- HSM startup -----
|
||||||
|
|
||||||
|
const { hsmService } = await getMigrationHsmService({ envConfig });
|
||||||
|
|
||||||
|
const hsmStatus = await isHsmActiveAndEnabled({
|
||||||
|
hsmService,
|
||||||
|
kmsRootConfigDAL,
|
||||||
|
licenseService
|
||||||
|
});
|
||||||
|
|
||||||
|
// if the encryption strategy is software - user needs to provide an encryption key
|
||||||
|
// if the encryption strategy is null AND the hsm is not configured - user needs to provide an encryption key
|
||||||
|
const needsEncryptionKey =
|
||||||
|
hsmStatus.rootKmsConfigEncryptionStrategy === RootKeyEncryptionStrategy.Software ||
|
||||||
|
(hsmStatus.rootKmsConfigEncryptionStrategy === null && !hsmStatus.isHsmConfigured);
|
||||||
|
|
||||||
|
if (needsEncryptionKey) {
|
||||||
|
if (!envConfig.ROOT_ENCRYPTION_KEY && !envConfig.ENCRYPTION_KEY) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message:
|
||||||
|
"Root KMS encryption strategy is set to software. Please set the ENCRYPTION_KEY environment variable and restart your deployment.\nYou can enable HSM encryption in the Server Console."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- KMS startup -----
|
||||||
|
|
||||||
const kmsService = kmsServiceFactory({
|
const kmsService = kmsServiceFactory({
|
||||||
kmsRootConfigDAL,
|
kmsRootConfigDAL,
|
||||||
keyStore,
|
keyStore,
|
||||||
@@ -115,27 +118,7 @@ export const getMigrationPITServices = async ({
|
|||||||
envConfig
|
envConfig
|
||||||
});
|
});
|
||||||
|
|
||||||
await hsmService.startService();
|
await kmsService.startService(hsmStatus);
|
||||||
await kmsService.startService();
|
|
||||||
|
|
||||||
const folderCommitService = folderCommitServiceFactory({
|
return { kmsService, hsmService };
|
||||||
folderCommitDAL,
|
|
||||||
folderCommitChangesDAL,
|
|
||||||
folderCheckpointDAL,
|
|
||||||
folderTreeCheckpointDAL,
|
|
||||||
userDAL,
|
|
||||||
identityDAL,
|
|
||||||
folderDAL,
|
|
||||||
folderVersionDAL,
|
|
||||||
secretVersionV2BridgeDAL,
|
|
||||||
projectDAL,
|
|
||||||
folderCheckpointResourcesDAL,
|
|
||||||
secretV2BridgeDAL,
|
|
||||||
folderTreeCheckpointResourcesDAL,
|
|
||||||
kmsService,
|
|
||||||
secretTagDAL,
|
|
||||||
resourceMetadataDAL
|
|
||||||
});
|
|
||||||
|
|
||||||
return { folderCommitService };
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ export const IdentitiesSchema = z.object({
|
|||||||
authMethod: z.string().nullable().optional(),
|
authMethod: z.string().nullable().optional(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
hasDeleteProtection: z.boolean().default(false)
|
hasDeleteProtection: z.boolean().default(false),
|
||||||
|
orgId: z.string().uuid()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TIdentities = z.infer<typeof IdentitiesSchema>;
|
export type TIdentities = z.infer<typeof IdentitiesSchema>;
|
||||||
|
|||||||
@@ -316,6 +316,12 @@ export enum ActionProjectType {
|
|||||||
Any = "any"
|
Any = "any"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum OrganizationActionScope {
|
||||||
|
ChildOrganization = "child-organization-only",
|
||||||
|
ParentOrganization = "parent-organization-only",
|
||||||
|
Any = "any"
|
||||||
|
}
|
||||||
|
|
||||||
export enum TemporaryPermissionMode {
|
export enum TemporaryPermissionMode {
|
||||||
Relative = "relative"
|
Relative = "relative"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,9 @@ export const OrganizationsSchema = z.object({
|
|||||||
maxSharedSecretLifetime: z.number().default(2592000).nullable().optional(),
|
maxSharedSecretLifetime: z.number().default(2592000).nullable().optional(),
|
||||||
maxSharedSecretViewLimit: z.number().nullable().optional(),
|
maxSharedSecretViewLimit: z.number().nullable().optional(),
|
||||||
googleSsoAuthEnforced: z.boolean().default(false),
|
googleSsoAuthEnforced: z.boolean().default(false),
|
||||||
googleSsoAuthLastUsed: z.date().nullable().optional()
|
googleSsoAuthLastUsed: z.date().nullable().optional(),
|
||||||
|
parentOrgId: z.string().uuid().nullable().optional(),
|
||||||
|
rootOrgId: z.string().uuid().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TOrganizations = z.infer<typeof OrganizationsSchema>;
|
export type TOrganizations = z.infer<typeof OrganizationsSchema>;
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ export const PamAccountsSchema = z.object({
|
|||||||
description: z.string().nullable().optional(),
|
description: z.string().nullable().optional(),
|
||||||
encryptedCredentials: zodBuffer,
|
encryptedCredentials: zodBuffer,
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date()
|
updatedAt: z.date(),
|
||||||
|
rotationEnabled: z.boolean().default(false),
|
||||||
|
rotationIntervalSeconds: z.number().nullable().optional(),
|
||||||
|
lastRotatedAt: z.date().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TPamAccounts = z.infer<typeof PamAccountsSchema>;
|
export type TPamAccounts = z.infer<typeof PamAccountsSchema>;
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ export const PamResourcesSchema = z.object({
|
|||||||
resourceType: z.string(),
|
resourceType: z.string(),
|
||||||
encryptedConnectionDetails: zodBuffer,
|
encryptedConnectionDetails: zodBuffer,
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date()
|
updatedAt: z.date(),
|
||||||
|
encryptedRotationAccountCredentials: zodBuffer.nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TPamResources = z.infer<typeof PamResourcesSchema>;
|
export type TPamResources = z.infer<typeof PamResourcesSchema>;
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { initEnvConfig } from "@app/lib/config/env";
|
import { initializeHsmModule } from "@app/ee/services/hsm/hsm-fns";
|
||||||
|
import { hsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
|
||||||
|
import { getHsmConfig, initEnvConfig } from "@app/lib/config/env";
|
||||||
import { initLogger, logger } from "@app/lib/logger";
|
import { initLogger, logger } from "@app/lib/logger";
|
||||||
|
import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
import { AuthMethod } from "../../services/auth/auth-type";
|
import { AuthMethod } from "../../services/auth/auth-type";
|
||||||
@@ -17,7 +20,21 @@ export async function seed(knex: Knex): Promise<void> {
|
|||||||
initLogger();
|
initLogger();
|
||||||
|
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
const superAdminDAL = superAdminDALFactory(knex);
|
||||||
await initEnvConfig(superAdminDAL, logger);
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(knex);
|
||||||
|
|
||||||
|
const hsmConfig = getHsmConfig(logger);
|
||||||
|
|
||||||
|
const hsmModule = initializeHsmModule(hsmConfig);
|
||||||
|
hsmModule.initialize();
|
||||||
|
|
||||||
|
const hsmService = hsmServiceFactory({
|
||||||
|
hsmModule: hsmModule.getModule(),
|
||||||
|
envConfig: hsmConfig
|
||||||
|
});
|
||||||
|
|
||||||
|
await hsmService.startService();
|
||||||
|
|
||||||
|
await initEnvConfig(hsmService, kmsRootConfigDAL, superAdminDAL, logger);
|
||||||
|
|
||||||
await knex(TableName.SuperAdmin).insert([
|
await knex(TableName.SuperAdmin).insert([
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { initEnvConfig } from "@app/lib/config/env";
|
import { initializeHsmModule } from "@app/ee/services/hsm/hsm-fns";
|
||||||
|
import { hsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
|
||||||
|
import { getHsmConfig, initEnvConfig } from "@app/lib/config/env";
|
||||||
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
|
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
|
||||||
import { generateUserSrpKeys } from "@app/lib/crypto/srp";
|
import { generateUserSrpKeys } from "@app/lib/crypto/srp";
|
||||||
import { initLogger, logger } from "@app/lib/logger";
|
import { initLogger, logger } from "@app/lib/logger";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { AuthMethod } from "@app/services/auth/auth-type";
|
import { AuthMethod } from "@app/services/auth/auth-type";
|
||||||
|
import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
import { membershipRoleDALFactory } from "@app/services/membership/membership-role-dal";
|
import { membershipRoleDALFactory } from "@app/services/membership/membership-role-dal";
|
||||||
import { membershipUserDALFactory } from "@app/services/membership-user/membership-user-dal";
|
import { membershipUserDALFactory } from "@app/services/membership-user/membership-user-dal";
|
||||||
import { assignWorkspaceKeysToMembers, createProjectKey } from "@app/services/project/project-fns";
|
import { assignWorkspaceKeysToMembers, createProjectKey } from "@app/services/project/project-fns";
|
||||||
@@ -192,7 +195,21 @@ export async function seed(knex: Knex): Promise<void> {
|
|||||||
initLogger();
|
initLogger();
|
||||||
|
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
const superAdminDAL = superAdminDALFactory(knex);
|
||||||
await initEnvConfig(superAdminDAL, logger);
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(knex);
|
||||||
|
|
||||||
|
const hsmConfig = getHsmConfig(logger);
|
||||||
|
|
||||||
|
const hsmModule = initializeHsmModule(hsmConfig);
|
||||||
|
hsmModule.initialize();
|
||||||
|
|
||||||
|
const hsmService = hsmServiceFactory({
|
||||||
|
hsmModule: hsmModule.getModule(),
|
||||||
|
envConfig: hsmConfig
|
||||||
|
});
|
||||||
|
|
||||||
|
await hsmService.startService();
|
||||||
|
|
||||||
|
await initEnvConfig(hsmService, kmsRootConfigDAL, superAdminDAL, logger);
|
||||||
|
|
||||||
const [project] = await knex(TableName.Project)
|
const [project] = await knex(TableName.Project)
|
||||||
.insert({
|
.insert({
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { initEnvConfig } from "@app/lib/config/env";
|
import { initializeHsmModule } from "@app/ee/services/hsm/hsm-fns";
|
||||||
|
import { hsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
|
||||||
|
import { getHsmConfig, initEnvConfig } from "@app/lib/config/env";
|
||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
import { initLogger, logger } from "@app/lib/logger";
|
import { initLogger, logger } from "@app/lib/logger";
|
||||||
|
import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
import { AccessScope, IdentityAuthMethod, OrgMembershipRole, ProjectMembershipRole, TableName } from "../schemas";
|
import { AccessScope, IdentityAuthMethod, OrgMembershipRole, ProjectMembershipRole, TableName } from "../schemas";
|
||||||
@@ -15,7 +18,20 @@ export async function seed(knex: Knex): Promise<void> {
|
|||||||
initLogger();
|
initLogger();
|
||||||
|
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
const superAdminDAL = superAdminDALFactory(knex);
|
||||||
await initEnvConfig(superAdminDAL, logger);
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(knex);
|
||||||
|
const hsmConfig = getHsmConfig(logger);
|
||||||
|
|
||||||
|
const hsmModule = initializeHsmModule(hsmConfig);
|
||||||
|
hsmModule.initialize();
|
||||||
|
|
||||||
|
const hsmService = hsmServiceFactory({
|
||||||
|
hsmModule: hsmModule.getModule(),
|
||||||
|
envConfig: hsmConfig
|
||||||
|
});
|
||||||
|
|
||||||
|
await hsmService.startService();
|
||||||
|
|
||||||
|
await initEnvConfig(hsmService, kmsRootConfigDAL, superAdminDAL, logger);
|
||||||
|
|
||||||
// Inserts seed entries
|
// Inserts seed entries
|
||||||
await knex(TableName.Identity).insert([
|
await knex(TableName.Identity).insert([
|
||||||
@@ -24,7 +40,8 @@ export async function seed(knex: Knex): Promise<void> {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
id: seedData1.machineIdentity.id,
|
id: seedData1.machineIdentity.id,
|
||||||
name: seedData1.machineIdentity.name,
|
name: seedData1.machineIdentity.name,
|
||||||
authMethod: IdentityAuthMethod.UNIVERSAL_AUTH
|
authMethod: IdentityAuthMethod.UNIVERSAL_AUTH,
|
||||||
|
orgId: seedData1.organization.id
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
const identityUa = await knex(TableName.IdentityUniversalAuth)
|
const identityUa = await knex(TableName.IdentityUniversalAuth)
|
||||||
|
|||||||
@@ -48,12 +48,14 @@ import { registerSshCertRouter } from "./ssh-certificate-router";
|
|||||||
import { registerSshCertificateTemplateRouter } from "./ssh-certificate-template-router";
|
import { registerSshCertificateTemplateRouter } from "./ssh-certificate-template-router";
|
||||||
import { registerSshHostGroupRouter } from "./ssh-host-group-router";
|
import { registerSshHostGroupRouter } from "./ssh-host-group-router";
|
||||||
import { registerSshHostRouter } from "./ssh-host-router";
|
import { registerSshHostRouter } from "./ssh-host-router";
|
||||||
|
import { registerSubOrgRouter } from "./sub-org-router";
|
||||||
import { registerTrustedIpRouter } from "./trusted-ip-router";
|
import { registerTrustedIpRouter } from "./trusted-ip-router";
|
||||||
import { registerUserAdditionalPrivilegeRouter } from "./user-additional-privilege-router";
|
import { registerUserAdditionalPrivilegeRouter } from "./user-additional-privilege-router";
|
||||||
|
|
||||||
export const registerV1EERoutes = async (server: FastifyZodProvider) => {
|
export const registerV1EERoutes = async (server: FastifyZodProvider) => {
|
||||||
// org role starts with organization
|
// org role starts with organization
|
||||||
await server.register(registerOrgRoleRouter, { prefix: "/organization" });
|
await server.register(registerOrgRoleRouter, { prefix: "/organization" });
|
||||||
|
await server.register(registerSubOrgRouter, { prefix: "/sub-organizations" });
|
||||||
await server.register(registerLicenseRouter, { prefix: "/organizations" });
|
await server.register(registerLicenseRouter, { prefix: "/organizations" });
|
||||||
|
|
||||||
// depreciated in favour of infisical workspace
|
// depreciated in favour of infisical workspace
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
|||||||
const plan = await server.services.license.getOrgPlan({
|
const plan = await server.services.license.getOrgPlan({
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.rootOrgId,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
orgId: req.params.organizationId,
|
orgId: req.params.organizationId,
|
||||||
refreshCache: req.query.refreshCache
|
refreshCache: req.query.refreshCache
|
||||||
|
|||||||
@@ -3,12 +3,35 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { AccessScope, OrgMembershipRole, OrgRolesSchema } from "@app/db/schemas";
|
import { AccessScope, OrgMembershipRole, OrgRolesSchema } from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { OrgPermissionSchema } from "@app/ee/services/permission/org-permission";
|
import { OrgPermissionSchema, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||||
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
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 { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
const INVALID_SUBORG_PERMISSIONS = [
|
||||||
|
OrgPermissionSubjects.Sso,
|
||||||
|
OrgPermissionSubjects.Ldap,
|
||||||
|
OrgPermissionSubjects.Scim,
|
||||||
|
OrgPermissionSubjects.GithubOrgSync,
|
||||||
|
OrgPermissionSubjects.GithubOrgSyncManual,
|
||||||
|
OrgPermissionSubjects.Billing,
|
||||||
|
OrgPermissionSubjects.SubOrganization
|
||||||
|
];
|
||||||
|
|
||||||
|
const validateSubOrganizationSubjects = (permissions: unknown) => {
|
||||||
|
const invalidPermissionSubjects = (permissions as { subject: OrgPermissionSubjects }[])
|
||||||
|
.filter((el) => INVALID_SUBORG_PERMISSIONS.includes(el.subject))
|
||||||
|
.map((el) => el.subject);
|
||||||
|
if (invalidPermissionSubjects.length) {
|
||||||
|
const deduplication = Array.from(new Set(invalidPermissionSubjects));
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `Suborganization contains invalid permission subjects: ${deduplication.join(",")}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@@ -37,6 +60,11 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
|
const isSubOrganization = req.permission.rootOrgId !== req.permission.orgId;
|
||||||
|
if (isSubOrganization) {
|
||||||
|
validateSubOrganizationSubjects(req.body.permissions);
|
||||||
|
}
|
||||||
|
|
||||||
const stringifiedPermissions = JSON.stringify(packRules(req.body.permissions));
|
const stringifiedPermissions = JSON.stringify(packRules(req.body.permissions));
|
||||||
const role = await server.services.role.createRole({
|
const role = await server.services.role.createRole({
|
||||||
permission: req.permission,
|
permission: req.permission,
|
||||||
@@ -133,6 +161,11 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
|
const isSubOrganization = req.permission.rootOrgId !== req.permission.orgId;
|
||||||
|
if (isSubOrganization && req.body.permissions) {
|
||||||
|
validateSubOrganizationSubjects(req.body.permissions);
|
||||||
|
}
|
||||||
|
|
||||||
const stringifiedPermissions = req.body.permissions ? JSON.stringify(packRules(req.body.permissions)) : undefined;
|
const stringifiedPermissions = req.body.permissions ? JSON.stringify(packRules(req.body.permissions)) : undefined;
|
||||||
const role = await server.services.role.updateRole({
|
const role = await server.services.role.updateRole({
|
||||||
permission: req.permission,
|
permission: req.permission,
|
||||||
|
|||||||
@@ -22,11 +22,15 @@ export const registerPamResourceEndpoints = <C extends TPamAccount>({
|
|||||||
folderId?: C["folderId"];
|
folderId?: C["folderId"];
|
||||||
name: C["name"];
|
name: C["name"];
|
||||||
description?: C["description"];
|
description?: C["description"];
|
||||||
|
rotationEnabled: C["rotationEnabled"];
|
||||||
|
rotationIntervalSeconds?: C["rotationIntervalSeconds"];
|
||||||
}>;
|
}>;
|
||||||
updateAccountSchema: z.ZodType<{
|
updateAccountSchema: z.ZodType<{
|
||||||
credentials?: C["credentials"];
|
credentials?: C["credentials"];
|
||||||
name?: C["name"];
|
name?: C["name"];
|
||||||
description?: C["description"];
|
description?: C["description"];
|
||||||
|
rotationEnabled?: C["rotationEnabled"];
|
||||||
|
rotationIntervalSeconds?: C["rotationIntervalSeconds"];
|
||||||
}>;
|
}>;
|
||||||
accountResponseSchema: z.ZodTypeAny;
|
accountResponseSchema: z.ZodTypeAny;
|
||||||
}) => {
|
}) => {
|
||||||
@@ -60,7 +64,9 @@ export const registerPamResourceEndpoints = <C extends TPamAccount>({
|
|||||||
resourceType,
|
resourceType,
|
||||||
folderId: req.body.folderId,
|
folderId: req.body.folderId,
|
||||||
name: req.body.name,
|
name: req.body.name,
|
||||||
description: req.body.description
|
description: req.body.description,
|
||||||
|
rotationEnabled: req.body.rotationEnabled,
|
||||||
|
rotationIntervalSeconds: req.body.rotationIntervalSeconds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -108,7 +114,9 @@ export const registerPamResourceEndpoints = <C extends TPamAccount>({
|
|||||||
resourceId: account.resourceId,
|
resourceId: account.resourceId,
|
||||||
resourceType,
|
resourceType,
|
||||||
name: req.body.name,
|
name: req.body.name,
|
||||||
description: req.body.description
|
description: req.body.description,
|
||||||
|
rotationEnabled: req.body.rotationEnabled,
|
||||||
|
rotationIntervalSeconds: req.body.rotationIntervalSeconds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { PamResource } from "@app/ee/services/pam-resource/pam-resource-enums";
|
import { PamResource } from "@app/ee/services/pam-resource/pam-resource-enums";
|
||||||
import {
|
import {
|
||||||
CreatePostgresResourceSchema,
|
CreatePostgresResourceSchema,
|
||||||
PostgresResourceSchema,
|
SanitizedPostgresResourceSchema,
|
||||||
UpdatePostgresResourceSchema
|
UpdatePostgresResourceSchema
|
||||||
} from "@app/ee/services/pam-resource/postgres/postgres-resource-schemas";
|
} from "@app/ee/services/pam-resource/postgres/postgres-resource-schemas";
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ export const PAM_RESOURCE_REGISTER_ROUTER_MAP: Record<PamResource, (server: Fast
|
|||||||
registerPamResourceEndpoints({
|
registerPamResourceEndpoints({
|
||||||
server,
|
server,
|
||||||
resourceType: PamResource.Postgres,
|
resourceType: PamResource.Postgres,
|
||||||
resourceResponseSchema: PostgresResourceSchema,
|
resourceResponseSchema: SanitizedPostgresResourceSchema,
|
||||||
createResourceSchema: CreatePostgresResourceSchema,
|
createResourceSchema: CreatePostgresResourceSchema,
|
||||||
updateResourceSchema: UpdatePostgresResourceSchema
|
updateResourceSchema: UpdatePostgresResourceSchema
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -21,11 +21,13 @@ export const registerPamResourceEndpoints = <T extends TPamResource>({
|
|||||||
connectionDetails: T["connectionDetails"];
|
connectionDetails: T["connectionDetails"];
|
||||||
gatewayId: T["gatewayId"];
|
gatewayId: T["gatewayId"];
|
||||||
name: T["name"];
|
name: T["name"];
|
||||||
|
rotationAccountCredentials?: T["rotationAccountCredentials"];
|
||||||
}>;
|
}>;
|
||||||
updateResourceSchema: z.ZodType<{
|
updateResourceSchema: z.ZodType<{
|
||||||
connectionDetails?: T["connectionDetails"];
|
connectionDetails?: T["connectionDetails"];
|
||||||
gatewayId?: T["gatewayId"];
|
gatewayId?: T["gatewayId"];
|
||||||
name?: T["name"];
|
name?: T["name"];
|
||||||
|
rotationAccountCredentials?: T["rotationAccountCredentials"];
|
||||||
}>;
|
}>;
|
||||||
resourceResponseSchema: z.ZodTypeAny;
|
resourceResponseSchema: z.ZodTypeAny;
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ import { z } from "zod";
|
|||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import {
|
import {
|
||||||
PostgresResourceListItemSchema,
|
PostgresResourceListItemSchema,
|
||||||
PostgresResourceSchema
|
SanitizedPostgresResourceSchema
|
||||||
} from "@app/ee/services/pam-resource/postgres/postgres-resource-schemas";
|
} from "@app/ee/services/pam-resource/postgres/postgres-resource-schemas";
|
||||||
import { readLimit } from "@app/server/config/rateLimiter";
|
import { readLimit } 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";
|
||||||
|
|
||||||
// Use z.union([...]) when more resources are added
|
// Use z.union([...]) when more resources are added
|
||||||
const ResourceSchema = PostgresResourceSchema;
|
const SanitizedResourceSchema = SanitizedPostgresResourceSchema;
|
||||||
|
|
||||||
const ResourceOptionsSchema = z.discriminatedUnion("resource", [PostgresResourceListItemSchema]);
|
const ResourceOptionsSchema = z.discriminatedUnion("resource", [PostgresResourceListItemSchema]);
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ export const registerPamResourceRouter = async (server: FastifyZodProvider) => {
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
resources: ResourceSchema.array()
|
resources: SanitizedResourceSchema.array()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,163 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { OrganizationsSchema } from "@app/db/schemas";
|
||||||
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
|
import { ApiDocsTags, SUB_ORGANIZATIONS } from "@app/lib/api-docs";
|
||||||
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
|
import { slugSchema } from "@app/server/lib/schemas";
|
||||||
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
const sanitizedSubOrganizationSchema = OrganizationsSchema.pick({
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
slug: true,
|
||||||
|
createdAt: true,
|
||||||
|
updatedAt: true,
|
||||||
|
parentOrgId: true
|
||||||
|
});
|
||||||
|
|
||||||
|
export const registerSubOrgRouter = async (server: FastifyZodProvider) => {
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
hide: false,
|
||||||
|
tags: [ApiDocsTags.SubOrganizations],
|
||||||
|
description: "Create a sub organization",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
body: z.object({
|
||||||
|
name: slugSchema().describe(SUB_ORGANIZATIONS.CREATE.name)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
organization: sanitizedSubOrganizationSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { organization } = await server.services.subOrganization.createSubOrg({
|
||||||
|
name: req.body.name,
|
||||||
|
permissionActor: req.permission
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
event: {
|
||||||
|
type: EventType.CREATE_SUB_ORGANIZATION,
|
||||||
|
metadata: {
|
||||||
|
name: req.body.name,
|
||||||
|
organizationId: organization.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { organization };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
hide: false,
|
||||||
|
tags: [ApiDocsTags.SubOrganizations],
|
||||||
|
description: "List of sub organizations",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
querystring: z.object({
|
||||||
|
limit: z.coerce.number().min(1).max(1000).default(25).describe(SUB_ORGANIZATIONS.LIST.limit),
|
||||||
|
offset: z.coerce.number().min(0).default(0).describe(SUB_ORGANIZATIONS.LIST.offset),
|
||||||
|
isAccessible: z
|
||||||
|
.enum(["true", "false"])
|
||||||
|
.optional()
|
||||||
|
.transform((value) => value === "true")
|
||||||
|
.describe(SUB_ORGANIZATIONS.LIST.isAccessible)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
organizations: sanitizedSubOrganizationSchema.array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { organizations } = await server.services.subOrganization.listSubOrgs({
|
||||||
|
permissionActor: req.permission,
|
||||||
|
data: {
|
||||||
|
limit: req.query.limit,
|
||||||
|
offset: req.query.offset,
|
||||||
|
isAccessible: req.query.isAccessible
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { organizations };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "PATCH",
|
||||||
|
url: "/:subOrgId",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
hide: false,
|
||||||
|
tags: [ApiDocsTags.SubOrganizations],
|
||||||
|
description: "Update a sub organization",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
params: z.object({
|
||||||
|
subOrgId: z.string().trim().describe(SUB_ORGANIZATIONS.UPDATE.subOrgId)
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
name: slugSchema().describe(SUB_ORGANIZATIONS.UPDATE.name)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
organization: sanitizedSubOrganizationSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { organization } = await server.services.subOrganization.updateSubOrg({
|
||||||
|
subOrgId: req.params.subOrgId,
|
||||||
|
name: req.body.name,
|
||||||
|
permissionActor: req.permission
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
event: {
|
||||||
|
type: EventType.UPDATE_SUB_ORGANIZATION,
|
||||||
|
metadata: {
|
||||||
|
name: req.body.name,
|
||||||
|
organizationId: organization.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { organization };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import { AxiosError } from "axios";
|
import { AxiosError } from "axios";
|
||||||
|
|
||||||
import { TAuditLogs } from "@app/db/schemas";
|
import { OrganizationActionScope, TAuditLogs } from "@app/db/schemas";
|
||||||
import {
|
import {
|
||||||
decryptLogStream,
|
decryptLogStream,
|
||||||
decryptLogStreamCredentials,
|
decryptLogStreamCredentials,
|
||||||
@@ -45,13 +45,14 @@ export const auditLogStreamServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
actor.type,
|
scope: OrganizationActionScope.Any,
|
||||||
actor.id,
|
actor: actor.type,
|
||||||
actor.orgId,
|
actorId: actor.id,
|
||||||
actor.authMethod,
|
orgId: actor.orgId,
|
||||||
actor.orgId
|
actorAuthMethod: actor.authMethod,
|
||||||
);
|
actorOrgId: actor.orgId
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Settings);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Settings);
|
||||||
|
|
||||||
@@ -94,13 +95,14 @@ export const auditLogStreamServiceFactory = ({
|
|||||||
const logStream = await auditLogStreamDAL.findById(logStreamId);
|
const logStream = await auditLogStreamDAL.findById(logStreamId);
|
||||||
if (!logStream) throw new NotFoundError({ message: `Audit Log Stream with ID '${logStreamId}' not found` });
|
if (!logStream) throw new NotFoundError({ message: `Audit Log Stream with ID '${logStreamId}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
actor.type,
|
scope: OrganizationActionScope.Any,
|
||||||
actor.id,
|
actor: actor.type,
|
||||||
actor.orgId,
|
actorId: actor.id,
|
||||||
actor.authMethod,
|
orgId: actor.orgId,
|
||||||
logStream.orgId
|
actorAuthMethod: actor.authMethod,
|
||||||
);
|
actorOrgId: actor.orgId
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings);
|
||||||
|
|
||||||
@@ -160,13 +162,14 @@ export const auditLogStreamServiceFactory = ({
|
|||||||
const logStream = await auditLogStreamDAL.findById(logStreamId);
|
const logStream = await auditLogStreamDAL.findById(logStreamId);
|
||||||
if (!logStream) throw new NotFoundError({ message: `Audit Log Stream with ID '${logStreamId}' not found` });
|
if (!logStream) throw new NotFoundError({ message: `Audit Log Stream with ID '${logStreamId}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
actor.type,
|
scope: OrganizationActionScope.Any,
|
||||||
actor.id,
|
actor: actor.type,
|
||||||
actor.orgId,
|
actorId: actor.id,
|
||||||
actor.authMethod,
|
orgId: actor.orgId,
|
||||||
logStream.orgId
|
actorAuthMethod: actor.authMethod,
|
||||||
);
|
actorOrgId: actor.orgId
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Settings);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Settings);
|
||||||
|
|
||||||
@@ -185,14 +188,14 @@ export const auditLogStreamServiceFactory = ({
|
|||||||
const logStream = await auditLogStreamDAL.findById(logStreamId);
|
const logStream = await auditLogStreamDAL.findById(logStreamId);
|
||||||
|
|
||||||
if (!logStream) throw new NotFoundError({ message: `Audit log stream with ID '${logStreamId}' not found` });
|
if (!logStream) throw new NotFoundError({ message: `Audit log stream with ID '${logStreamId}' not found` });
|
||||||
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
scope: OrganizationActionScope.Any,
|
||||||
actor.type,
|
actor: actor.type,
|
||||||
actor.id,
|
actorId: actor.id,
|
||||||
logStream.orgId,
|
orgId: actor.orgId,
|
||||||
actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actor.orgId
|
actorOrgId: actor.orgId
|
||||||
);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Settings);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Settings);
|
||||||
|
|
||||||
@@ -206,13 +209,14 @@ export const auditLogStreamServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const list = async (actor: OrgServiceActor) => {
|
const list = async (actor: OrgServiceActor) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
actor.type,
|
scope: OrganizationActionScope.Any,
|
||||||
actor.id,
|
actor: actor.type,
|
||||||
actor.orgId,
|
actorId: actor.id,
|
||||||
actor.authMethod,
|
orgId: actor.orgId,
|
||||||
actor.orgId
|
actorAuthMethod: actor.authMethod,
|
||||||
);
|
actorOrgId: actor.orgId
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Settings);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Settings);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import { requestContext } from "@fastify/request-context";
|
import { requestContext } from "@fastify/request-context";
|
||||||
|
|
||||||
import { ActionProjectType } from "@app/db/schemas";
|
import { ActionProjectType, OrganizationActionScope } from "@app/db/schemas";
|
||||||
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 { ActorType } from "@app/services/auth/auth-type";
|
import { ActorType } from "@app/services/auth/auth-type";
|
||||||
@@ -47,13 +47,14 @@ export const auditLogServiceFactory = ({
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// Organization-wide logs
|
// Organization-wide logs
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionAuditLogsActions.Read,
|
OrgPermissionAuditLogsActions.Read,
|
||||||
|
|||||||
@@ -173,6 +173,9 @@ export enum EventType {
|
|||||||
UPDATE_TOKEN_IDENTITY_TOKEN_AUTH = "update-token-identity-token-auth",
|
UPDATE_TOKEN_IDENTITY_TOKEN_AUTH = "update-token-identity-token-auth",
|
||||||
GET_TOKENS_IDENTITY_TOKEN_AUTH = "get-tokens-identity-token-auth",
|
GET_TOKENS_IDENTITY_TOKEN_AUTH = "get-tokens-identity-token-auth",
|
||||||
|
|
||||||
|
CREATE_SUB_ORGANIZATION = "create-sub-organization",
|
||||||
|
UPDATE_SUB_ORGANIZATION = "update-sub-organization",
|
||||||
|
|
||||||
ADD_IDENTITY_TOKEN_AUTH = "add-identity-token-auth",
|
ADD_IDENTITY_TOKEN_AUTH = "add-identity-token-auth",
|
||||||
UPDATE_IDENTITY_TOKEN_AUTH = "update-identity-token-auth",
|
UPDATE_IDENTITY_TOKEN_AUTH = "update-identity-token-auth",
|
||||||
GET_IDENTITY_TOKEN_AUTH = "get-identity-token-auth",
|
GET_IDENTITY_TOKEN_AUTH = "get-identity-token-auth",
|
||||||
@@ -524,6 +527,8 @@ export enum EventType {
|
|||||||
PAM_ACCOUNT_CREATE = "pam-account-create",
|
PAM_ACCOUNT_CREATE = "pam-account-create",
|
||||||
PAM_ACCOUNT_UPDATE = "pam-account-update",
|
PAM_ACCOUNT_UPDATE = "pam-account-update",
|
||||||
PAM_ACCOUNT_DELETE = "pam-account-delete",
|
PAM_ACCOUNT_DELETE = "pam-account-delete",
|
||||||
|
PAM_ACCOUNT_CREDENTIAL_ROTATION = "pam-account-credential-rotation",
|
||||||
|
PAM_ACCOUNT_CREDENTIAL_ROTATION_FAILED = "pam-account-credential-rotation-failed",
|
||||||
PAM_RESOURCE_LIST = "pam-resource-list",
|
PAM_RESOURCE_LIST = "pam-resource-list",
|
||||||
PAM_RESOURCE_GET = "pam-resource-get",
|
PAM_RESOURCE_GET = "pam-resource-get",
|
||||||
PAM_RESOURCE_CREATE = "pam-resource-create",
|
PAM_RESOURCE_CREATE = "pam-resource-create",
|
||||||
@@ -616,6 +621,22 @@ interface GetSecretsEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface CreateSubOrganizationEvent {
|
||||||
|
type: EventType.CREATE_SUB_ORGANIZATION;
|
||||||
|
metadata: {
|
||||||
|
name: string;
|
||||||
|
organizationId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UpdateSubOrganizationEvent {
|
||||||
|
type: EventType.UPDATE_SUB_ORGANIZATION;
|
||||||
|
metadata: {
|
||||||
|
name: string;
|
||||||
|
organizationId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
type TSecretMetadata = { key: string; value: string }[];
|
type TSecretMetadata = { key: string; value: string }[];
|
||||||
|
|
||||||
interface GetSecretEvent {
|
interface GetSecretEvent {
|
||||||
@@ -3896,6 +3917,8 @@ interface PamAccountCreateEvent {
|
|||||||
folderId?: string | null;
|
folderId?: string | null;
|
||||||
name: string;
|
name: string;
|
||||||
description?: string | null;
|
description?: string | null;
|
||||||
|
rotationEnabled: boolean;
|
||||||
|
rotationIntervalSeconds?: number | null;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3907,6 +3930,8 @@ interface PamAccountUpdateEvent {
|
|||||||
resourceType: string;
|
resourceType: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
description?: string | null;
|
description?: string | null;
|
||||||
|
rotationEnabled?: boolean;
|
||||||
|
rotationIntervalSeconds?: number | null;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3920,6 +3945,27 @@ interface PamAccountDeleteEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface PamAccountCredentialRotationEvent {
|
||||||
|
type: EventType.PAM_ACCOUNT_CREDENTIAL_ROTATION;
|
||||||
|
metadata: {
|
||||||
|
accountName: string;
|
||||||
|
accountId: string;
|
||||||
|
resourceId: string;
|
||||||
|
resourceType: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PamAccountCredentialRotationFailedEvent {
|
||||||
|
type: EventType.PAM_ACCOUNT_CREDENTIAL_ROTATION_FAILED;
|
||||||
|
metadata: {
|
||||||
|
accountName: string;
|
||||||
|
accountId: string;
|
||||||
|
resourceId: string;
|
||||||
|
resourceType: string;
|
||||||
|
errorMessage: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface PamResourceListEvent {
|
interface PamResourceListEvent {
|
||||||
type: EventType.PAM_RESOURCE_LIST;
|
type: EventType.PAM_RESOURCE_LIST;
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -3964,6 +4010,8 @@ interface PamResourceDeleteEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type Event =
|
export type Event =
|
||||||
|
| CreateSubOrganizationEvent
|
||||||
|
| UpdateSubOrganizationEvent
|
||||||
| GetSecretsEvent
|
| GetSecretsEvent
|
||||||
| GetSecretEvent
|
| GetSecretEvent
|
||||||
| CreateSecretEvent
|
| CreateSecretEvent
|
||||||
@@ -4319,6 +4367,8 @@ export type Event =
|
|||||||
| PamAccountCreateEvent
|
| PamAccountCreateEvent
|
||||||
| PamAccountUpdateEvent
|
| PamAccountUpdateEvent
|
||||||
| PamAccountDeleteEvent
|
| PamAccountDeleteEvent
|
||||||
|
| PamAccountCredentialRotationEvent
|
||||||
|
| PamAccountCredentialRotationFailedEvent
|
||||||
| PamResourceListEvent
|
| PamResourceListEvent
|
||||||
| PamResourceGetEvent
|
| PamResourceGetEvent
|
||||||
| PamResourceCreateEvent
|
| PamResourceCreateEvent
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ForbiddenError, subject } from "@casl/ability";
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
|
|
||||||
import { ActionProjectType } from "@app/db/schemas";
|
import { ActionProjectType, OrganizationActionScope } from "@app/db/schemas";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import {
|
import {
|
||||||
@@ -134,13 +134,14 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
isGatewayV1 = false;
|
isGatewayV1 = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission: orgPermission } = await permissionService.getOrgPermission(
|
const { permission: orgPermission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
gateway?.orgId ?? gatewayv2?.orgId,
|
orgId: gateway?.orgId || gatewayv2?.orgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(orgPermission).throwUnlessCan(
|
ForbiddenError.from(orgPermission).throwUnlessCan(
|
||||||
OrgPermissionGatewayActions.AttachGateways,
|
OrgPermissionGatewayActions.AttachGateways,
|
||||||
@@ -297,13 +298,14 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
isGatewayV1 = false;
|
isGatewayV1 = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission: orgPermission } = await permissionService.getOrgPermission(
|
const { permission: orgPermission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: gateway?.orgId || gatewayv2?.orgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(orgPermission).throwUnlessCan(
|
ForbiddenError.from(orgPermission).throwUnlessCan(
|
||||||
OrgPermissionGatewayActions.AttachGateways,
|
OrgPermissionGatewayActions.AttachGateways,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { STSServiceException } from "@aws-sdk/client-sts";
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
|
|
||||||
|
import { OrganizationActionScope } from "@app/db/schemas";
|
||||||
import { BadRequestError, InternalServerError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, InternalServerError, NotFoundError } from "@app/lib/errors";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { TKmsKeyDALFactory } from "@app/services/kms/kms-key-dal";
|
import { TKmsKeyDALFactory } from "@app/services/kms/kms-key-dal";
|
||||||
@@ -51,13 +52,14 @@ export const externalKmsServiceFactory = ({
|
|||||||
actorOrgId,
|
actorOrgId,
|
||||||
actorAuthMethod
|
actorAuthMethod
|
||||||
}: TCreateExternalKmsDTO) => {
|
}: TCreateExternalKmsDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Kms);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Kms);
|
||||||
const plan = await licenseService.getPlan(actorOrgId);
|
const plan = await licenseService.getPlan(actorOrgId);
|
||||||
@@ -154,13 +156,14 @@ export const externalKmsServiceFactory = ({
|
|||||||
actorAuthMethod
|
actorAuthMethod
|
||||||
}: TUpdateExternalKmsDTO) => {
|
}: TUpdateExternalKmsDTO) => {
|
||||||
const kmsDoc = await kmsDAL.findById(kmsId);
|
const kmsDoc = await kmsDAL.findById(kmsId);
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
kmsDoc.orgId,
|
orgId: kmsDoc.orgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Kms);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Kms);
|
||||||
|
|
||||||
const plan = await licenseService.getPlan(kmsDoc.orgId);
|
const plan = await licenseService.getPlan(kmsDoc.orgId);
|
||||||
@@ -257,13 +260,14 @@ export const externalKmsServiceFactory = ({
|
|||||||
|
|
||||||
const deleteById = async ({ actor, id: kmsId, actorId, actorOrgId, actorAuthMethod }: TDeleteExternalKmsDTO) => {
|
const deleteById = async ({ actor, id: kmsId, actorId, actorOrgId, actorAuthMethod }: TDeleteExternalKmsDTO) => {
|
||||||
const kmsDoc = await kmsDAL.findById(kmsId);
|
const kmsDoc = await kmsDAL.findById(kmsId);
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
kmsDoc.orgId,
|
orgId: kmsDoc.orgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Kms);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Kms);
|
||||||
|
|
||||||
const externalKmsDoc = await externalKmsDAL.findOne({ kmsKeyId: kmsDoc.id });
|
const externalKmsDoc = await externalKmsDAL.findOne({ kmsKeyId: kmsDoc.id });
|
||||||
@@ -278,13 +282,14 @@ export const externalKmsServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const list = async ({ actor, actorId, actorOrgId, actorAuthMethod }: TListExternalKmsDTO) => {
|
const list = async ({ actor, actorId, actorOrgId, actorAuthMethod }: TListExternalKmsDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Kms);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Kms);
|
||||||
|
|
||||||
const externalKmsDocs = await externalKmsDAL.find({ orgId: actorOrgId });
|
const externalKmsDocs = await externalKmsDAL.find({ orgId: actorOrgId });
|
||||||
@@ -294,13 +299,14 @@ export const externalKmsServiceFactory = ({
|
|||||||
|
|
||||||
const findById = async ({ actor, actorId, actorOrgId, actorAuthMethod, id: kmsId }: TGetExternalKmsByIdDTO) => {
|
const findById = async ({ actor, actorId, actorOrgId, actorAuthMethod, id: kmsId }: TGetExternalKmsByIdDTO) => {
|
||||||
const kmsDoc = await kmsDAL.findById(kmsId);
|
const kmsDoc = await kmsDAL.findById(kmsId);
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
kmsDoc.orgId,
|
orgId: kmsDoc.orgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Kms);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Kms);
|
||||||
|
|
||||||
@@ -342,13 +348,14 @@ export const externalKmsServiceFactory = ({
|
|||||||
name: kmsName
|
name: kmsName
|
||||||
}: TGetExternalKmsBySlugDTO) => {
|
}: TGetExternalKmsBySlugDTO) => {
|
||||||
const kmsDoc = await kmsDAL.findOne({ name: kmsName, orgId: actorOrgId });
|
const kmsDoc = await kmsDAL.findOne({ name: kmsName, orgId: actorOrgId });
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
kmsDoc.orgId,
|
orgId: kmsDoc.orgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Kms);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Kms);
|
||||||
|
|
||||||
const externalKmsDoc = await externalKmsDAL.findOne({ kmsKeyId: kmsDoc.id });
|
const externalKmsDoc = await externalKmsDAL.findOne({ kmsKeyId: kmsDoc.id });
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import net from "node:net";
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import * as x509 from "@peculiar/x509";
|
import * as x509 from "@peculiar/x509";
|
||||||
|
|
||||||
import { OrgMembershipRole, TRelays } from "@app/db/schemas";
|
import { OrganizationActionScope, OrgMembershipRole, TRelays } from "@app/db/schemas";
|
||||||
import { PgSqlLock } from "@app/keystore/keystore";
|
import { PgSqlLock } from "@app/keystore/keystore";
|
||||||
import { crypto } from "@app/lib/crypto";
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { DatabaseErrorCode } from "@app/lib/error-codes";
|
import { DatabaseErrorCode } from "@app/lib/error-codes";
|
||||||
@@ -73,13 +73,14 @@ export const gatewayV2ServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
ActorType.IDENTITY,
|
scope: OrganizationActionScope.Any,
|
||||||
|
actor: ActorType.IDENTITY,
|
||||||
actorId,
|
actorId,
|
||||||
orgId,
|
orgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
orgId
|
actorOrgId: orgId
|
||||||
);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionGatewayActions.CreateGateways,
|
OrgPermissionGatewayActions.CreateGateways,
|
||||||
@@ -258,13 +259,14 @@ export const gatewayV2ServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const listGateways = async ({ orgPermission }: { orgPermission: OrgServiceActor }) => {
|
const listGateways = async ({ orgPermission }: { orgPermission: OrgServiceActor }) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
orgPermission.type,
|
actor: orgPermission.type,
|
||||||
orgPermission.id,
|
actorId: orgPermission.id,
|
||||||
orgPermission.orgId,
|
orgId: orgPermission.orgId,
|
||||||
orgPermission.authMethod,
|
actorAuthMethod: orgPermission.authMethod,
|
||||||
orgPermission.orgId
|
actorOrgId: orgPermission.orgId,
|
||||||
);
|
scope: OrganizationActionScope.Any
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionGatewayActions.ListGateways,
|
OrgPermissionGatewayActions.ListGateways,
|
||||||
@@ -815,13 +817,14 @@ export const gatewayV2ServiceFactory = ({
|
|||||||
throw new NotFoundError({ message: `Gateway ${id} not found` });
|
throw new NotFoundError({ message: `Gateway ${id} not found` });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
orgPermission.type,
|
actor: orgPermission.type,
|
||||||
orgPermission.id,
|
actorId: orgPermission.id,
|
||||||
gateway.orgId,
|
orgId: gateway.orgId,
|
||||||
orgPermission.authMethod,
|
actorAuthMethod: orgPermission.authMethod,
|
||||||
orgPermission.orgId
|
actorOrgId: orgPermission.orgId,
|
||||||
);
|
scope: OrganizationActionScope.Any
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionGatewayActions.DeleteGateways,
|
OrgPermissionGatewayActions.DeleteGateways,
|
||||||
@@ -845,13 +848,14 @@ export const gatewayV2ServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getPamSessionKey = async ({ orgPermission }: { orgPermission: OrgServiceActor }) => {
|
const getPamSessionKey = async ({ orgPermission }: { orgPermission: OrgServiceActor }) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
orgPermission.type,
|
actor: orgPermission.type,
|
||||||
orgPermission.id,
|
actorId: orgPermission.id,
|
||||||
orgPermission.orgId,
|
orgId: orgPermission.orgId,
|
||||||
orgPermission.authMethod,
|
actorAuthMethod: orgPermission.authMethod,
|
||||||
orgPermission.orgId
|
actorOrgId: orgPermission.orgId,
|
||||||
);
|
scope: OrganizationActionScope.Any
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionGatewayActions.CreateGateways,
|
OrgPermissionGatewayActions.CreateGateways,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { ForbiddenError } from "@casl/ability";
|
|||||||
import * as x509 from "@peculiar/x509";
|
import * as x509 from "@peculiar/x509";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { OrganizationActionScope } from "@app/db/schemas";
|
||||||
import { KeyStorePrefixes, PgSqlLock, TKeyStoreFactory } from "@app/keystore/keystore";
|
import { KeyStorePrefixes, PgSqlLock, TKeyStoreFactory } from "@app/keystore/keystore";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
@@ -68,13 +69,14 @@ export const gatewayServiceFactory = ({
|
|||||||
"Gateway handshake failed due to organization plan restrictions. Please upgrade your instance to Infisical's Enterprise plan."
|
"Gateway handshake failed due to organization plan restrictions. Please upgrade your instance to Infisical's Enterprise plan."
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
ActorType.IDENTITY,
|
actor: ActorType.IDENTITY,
|
||||||
actorId,
|
actorId,
|
||||||
orgId,
|
orgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
orgId
|
actorOrgId: orgId,
|
||||||
);
|
scope: OrganizationActionScope.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionGatewayActions.CreateGateways,
|
OrgPermissionGatewayActions.CreateGateways,
|
||||||
OrgPermissionSubjects.Gateway
|
OrgPermissionSubjects.Gateway
|
||||||
@@ -480,13 +482,14 @@ export const gatewayServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const listGateways = async ({ orgPermission }: TListGatewaysDTO) => {
|
const listGateways = async ({ orgPermission }: TListGatewaysDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
orgPermission.type,
|
actor: orgPermission.type,
|
||||||
orgPermission.id,
|
actorId: orgPermission.id,
|
||||||
orgPermission.orgId,
|
orgId: orgPermission.orgId,
|
||||||
orgPermission.authMethod,
|
actorAuthMethod: orgPermission.authMethod,
|
||||||
orgPermission.orgId
|
actorOrgId: orgPermission.orgId,
|
||||||
);
|
scope: OrganizationActionScope.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionGatewayActions.ListGateways,
|
OrgPermissionGatewayActions.ListGateways,
|
||||||
OrgPermissionSubjects.Gateway
|
OrgPermissionSubjects.Gateway
|
||||||
@@ -501,13 +504,14 @@ export const gatewayServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getGatewayById = async ({ orgPermission, id }: TGetGatewayByIdDTO) => {
|
const getGatewayById = async ({ orgPermission, id }: TGetGatewayByIdDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
orgPermission.type,
|
actor: orgPermission.type,
|
||||||
orgPermission.id,
|
actorId: orgPermission.id,
|
||||||
orgPermission.orgId,
|
orgId: orgPermission.orgId,
|
||||||
orgPermission.authMethod,
|
actorAuthMethod: orgPermission.authMethod,
|
||||||
orgPermission.orgId
|
actorOrgId: orgPermission.orgId,
|
||||||
);
|
scope: OrganizationActionScope.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionGatewayActions.ListGateways,
|
OrgPermissionGatewayActions.ListGateways,
|
||||||
OrgPermissionSubjects.Gateway
|
OrgPermissionSubjects.Gateway
|
||||||
@@ -521,13 +525,14 @@ export const gatewayServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateGatewayById = async ({ orgPermission, id, name }: TUpdateGatewayByIdDTO) => {
|
const updateGatewayById = async ({ orgPermission, id, name }: TUpdateGatewayByIdDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
orgPermission.type,
|
actor: orgPermission.type,
|
||||||
orgPermission.id,
|
actorId: orgPermission.id,
|
||||||
orgPermission.orgId,
|
orgId: orgPermission.orgId,
|
||||||
orgPermission.authMethod,
|
actorAuthMethod: orgPermission.authMethod,
|
||||||
orgPermission.orgId
|
actorOrgId: orgPermission.orgId,
|
||||||
);
|
scope: OrganizationActionScope.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionGatewayActions.EditGateways,
|
OrgPermissionGatewayActions.EditGateways,
|
||||||
OrgPermissionSubjects.Gateway
|
OrgPermissionSubjects.Gateway
|
||||||
@@ -542,13 +547,14 @@ export const gatewayServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const deleteGatewayById = async ({ orgPermission, id }: TGetGatewayByIdDTO) => {
|
const deleteGatewayById = async ({ orgPermission, id }: TGetGatewayByIdDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
orgPermission.type,
|
actor: orgPermission.type,
|
||||||
orgPermission.id,
|
actorId: orgPermission.id,
|
||||||
orgPermission.orgId,
|
orgId: orgPermission.orgId,
|
||||||
orgPermission.authMethod,
|
actorAuthMethod: orgPermission.authMethod,
|
||||||
orgPermission.orgId
|
actorOrgId: orgPermission.orgId,
|
||||||
);
|
scope: OrganizationActionScope.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionGatewayActions.DeleteGateways,
|
OrgPermissionGatewayActions.DeleteGateways,
|
||||||
OrgPermissionSubjects.Gateway
|
OrgPermissionSubjects.Gateway
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { paginateGraphql } from "@octokit/plugin-paginate-graphql";
|
|||||||
import { Octokit as OctokitRest } from "@octokit/rest";
|
import { Octokit as OctokitRest } from "@octokit/rest";
|
||||||
import RE2 from "re2";
|
import RE2 from "re2";
|
||||||
|
|
||||||
import { AccessScope, OrgMembershipRole } from "@app/db/schemas";
|
import { AccessScope, OrganizationActionScope, OrgMembershipRole } from "@app/db/schemas";
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { groupBy } from "@app/lib/fn";
|
import { groupBy } from "@app/lib/fn";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
@@ -104,13 +104,14 @@ export const githubOrgSyncServiceFactory = ({
|
|||||||
githubOrgAccessToken,
|
githubOrgAccessToken,
|
||||||
isActive
|
isActive
|
||||||
}: TCreateGithubOrgSyncDTO) => {
|
}: TCreateGithubOrgSyncDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
orgPermission.type,
|
scope: OrganizationActionScope.ParentOrganization,
|
||||||
orgPermission.id,
|
actor: orgPermission.type,
|
||||||
orgPermission.orgId,
|
actorId: orgPermission.id,
|
||||||
orgPermission.authMethod,
|
orgId: orgPermission.orgId,
|
||||||
orgPermission.orgId
|
actorAuthMethod: orgPermission.authMethod,
|
||||||
);
|
actorOrgId: orgPermission.orgId
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.GithubOrgSync);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.GithubOrgSync);
|
||||||
const plan = await licenseService.getPlan(orgPermission.orgId);
|
const plan = await licenseService.getPlan(orgPermission.orgId);
|
||||||
@@ -162,13 +163,14 @@ export const githubOrgSyncServiceFactory = ({
|
|||||||
githubOrgAccessToken,
|
githubOrgAccessToken,
|
||||||
isActive
|
isActive
|
||||||
}: TUpdateGithubOrgSyncDTO) => {
|
}: TUpdateGithubOrgSyncDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
orgPermission.type,
|
actor: orgPermission.type,
|
||||||
orgPermission.id,
|
scope: OrganizationActionScope.ParentOrganization,
|
||||||
orgPermission.orgId,
|
actorId: orgPermission.id,
|
||||||
orgPermission.authMethod,
|
orgId: orgPermission.orgId,
|
||||||
orgPermission.orgId
|
actorAuthMethod: orgPermission.authMethod,
|
||||||
);
|
actorOrgId: orgPermission.orgId
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.GithubOrgSync);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.GithubOrgSync);
|
||||||
const plan = await licenseService.getPlan(orgPermission.orgId);
|
const plan = await licenseService.getPlan(orgPermission.orgId);
|
||||||
@@ -226,13 +228,14 @@ export const githubOrgSyncServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const deleteGithubOrgSync = async ({ orgPermission }: TDeleteGithubOrgSyncDTO) => {
|
const deleteGithubOrgSync = async ({ orgPermission }: TDeleteGithubOrgSyncDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
orgPermission.type,
|
actor: orgPermission.type,
|
||||||
orgPermission.id,
|
actorId: orgPermission.id,
|
||||||
orgPermission.orgId,
|
orgId: orgPermission.orgId,
|
||||||
orgPermission.authMethod,
|
actorAuthMethod: orgPermission.authMethod,
|
||||||
orgPermission.orgId
|
actorOrgId: orgPermission.orgId,
|
||||||
);
|
scope: OrganizationActionScope.ParentOrganization
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.GithubOrgSync);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.GithubOrgSync);
|
||||||
|
|
||||||
@@ -256,13 +259,14 @@ export const githubOrgSyncServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getGithubOrgSync = async ({ orgPermission }: TDeleteGithubOrgSyncDTO) => {
|
const getGithubOrgSync = async ({ orgPermission }: TDeleteGithubOrgSyncDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
orgPermission.type,
|
actorId: orgPermission.id,
|
||||||
orgPermission.id,
|
actor: orgPermission.type,
|
||||||
orgPermission.orgId,
|
orgId: orgPermission.orgId,
|
||||||
orgPermission.authMethod,
|
actorAuthMethod: orgPermission.authMethod,
|
||||||
orgPermission.orgId
|
actorOrgId: orgPermission.orgId,
|
||||||
);
|
scope: OrganizationActionScope.ParentOrganization
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.GithubOrgSync);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.GithubOrgSync);
|
||||||
|
|
||||||
@@ -422,13 +426,14 @@ export const githubOrgSyncServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const validateGithubToken = async ({ orgPermission, githubOrgAccessToken }: TValidateGithubTokenDTO) => {
|
const validateGithubToken = async ({ orgPermission, githubOrgAccessToken }: TValidateGithubTokenDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
orgPermission.type,
|
actorId: orgPermission.id,
|
||||||
orgPermission.id,
|
actor: orgPermission.type,
|
||||||
orgPermission.orgId,
|
orgId: orgPermission.orgId,
|
||||||
orgPermission.authMethod,
|
actorAuthMethod: orgPermission.authMethod,
|
||||||
orgPermission.orgId
|
actorOrgId: orgPermission.orgId,
|
||||||
);
|
scope: OrganizationActionScope.ParentOrganization
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.GithubOrgSync);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.GithubOrgSync);
|
||||||
|
|
||||||
@@ -509,13 +514,14 @@ export const githubOrgSyncServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const syncAllTeams = async ({ orgPermission }: TSyncAllTeamsDTO): Promise<TSyncResult> => {
|
const syncAllTeams = async ({ orgPermission }: TSyncAllTeamsDTO): Promise<TSyncResult> => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
orgPermission.type,
|
scope: OrganizationActionScope.ParentOrganization,
|
||||||
orgPermission.id,
|
actor: orgPermission.type,
|
||||||
orgPermission.orgId,
|
orgId: orgPermission.orgId,
|
||||||
orgPermission.authMethod,
|
actorId: orgPermission.id,
|
||||||
orgPermission.orgId
|
actorAuthMethod: orgPermission.authMethod,
|
||||||
);
|
actorOrgId: orgPermission.orgId
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionActions.Edit,
|
OrgPermissionActions.Edit,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
|
|
||||||
import { AccessScope, OrgMembershipRole, TRoles } from "@app/db/schemas";
|
import { AccessScope, OrganizationActionScope, OrgMembershipRole, TRoles } from "@app/db/schemas";
|
||||||
import { TOidcConfigDALFactory } from "@app/ee/services/oidc/oidc-config-dal";
|
import { TOidcConfigDALFactory } from "@app/ee/services/oidc/oidc-config-dal";
|
||||||
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
@@ -73,13 +73,14 @@ export const groupServiceFactory = ({
|
|||||||
const createGroup = async ({ name, slug, role, actor, actorId, actorAuthMethod, actorOrgId }: TCreateGroupDTO) => {
|
const createGroup = async ({ name, slug, role, actor, actorId, actorAuthMethod, actorOrgId }: TCreateGroupDTO) => {
|
||||||
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Create, OrgPermissionSubjects.Groups);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Create, OrgPermissionSubjects.Groups);
|
||||||
|
|
||||||
const plan = await licenseService.getPlan(actorOrgId);
|
const plan = await licenseService.getPlan(actorOrgId);
|
||||||
@@ -167,13 +168,14 @@ export const groupServiceFactory = ({
|
|||||||
}: TUpdateGroupDTO) => {
|
}: TUpdateGroupDTO) => {
|
||||||
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Edit, OrgPermissionSubjects.Groups);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Edit, OrgPermissionSubjects.Groups);
|
||||||
|
|
||||||
@@ -270,13 +272,14 @@ export const groupServiceFactory = ({
|
|||||||
const deleteGroup = async ({ id, actor, actorId, actorAuthMethod, actorOrgId }: TDeleteGroupDTO) => {
|
const deleteGroup = async ({ id, actor, actorId, actorAuthMethod, actorOrgId }: TDeleteGroupDTO) => {
|
||||||
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Delete, OrgPermissionSubjects.Groups);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Delete, OrgPermissionSubjects.Groups);
|
||||||
|
|
||||||
const plan = await licenseService.getPlan(actorOrgId);
|
const plan = await licenseService.getPlan(actorOrgId);
|
||||||
@@ -297,17 +300,18 @@ export const groupServiceFactory = ({
|
|||||||
const getGroupById = async ({ id, actor, actorId, actorAuthMethod, actorOrgId }: TGetGroupByIdDTO) => {
|
const getGroupById = async ({ id, actor, actorId, actorAuthMethod, actorOrgId }: TGetGroupByIdDTO) => {
|
||||||
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Read, OrgPermissionSubjects.Groups);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Read, OrgPermissionSubjects.Groups);
|
||||||
|
|
||||||
const group = await groupDAL.findById(id);
|
const group = await groupDAL.findById(id);
|
||||||
if (!group) {
|
if (!group || group.orgId !== actorOrgId) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Cannot find group with ID ${id}`
|
message: `Cannot find group with ID ${id}`
|
||||||
});
|
});
|
||||||
@@ -330,13 +334,14 @@ export const groupServiceFactory = ({
|
|||||||
}: TListGroupUsersDTO) => {
|
}: TListGroupUsersDTO) => {
|
||||||
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Read, OrgPermissionSubjects.Groups);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Read, OrgPermissionSubjects.Groups);
|
||||||
|
|
||||||
const group = await groupDAL.findOne({
|
const group = await groupDAL.findOne({
|
||||||
@@ -365,13 +370,14 @@ export const groupServiceFactory = ({
|
|||||||
const addUserToGroup = async ({ id, username, actor, actorId, actorAuthMethod, actorOrgId }: TAddUserToGroupDTO) => {
|
const addUserToGroup = async ({ id, username, actor, actorId, actorAuthMethod, actorOrgId }: TAddUserToGroupDTO) => {
|
||||||
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Edit, OrgPermissionSubjects.Groups);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Edit, OrgPermissionSubjects.Groups);
|
||||||
|
|
||||||
// check if group with slug exists
|
// check if group with slug exists
|
||||||
@@ -451,13 +457,14 @@ export const groupServiceFactory = ({
|
|||||||
}: TRemoveUserFromGroupDTO) => {
|
}: TRemoveUserFromGroupDTO) => {
|
||||||
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
scope: OrganizationActionScope.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Edit, OrgPermissionSubjects.Groups);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Edit, OrgPermissionSubjects.Groups);
|
||||||
|
|
||||||
// check if group with slug exists
|
// check if group with slug exists
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
import * as pkcs11js from "pkcs11js";
|
import * as pkcs11js from "pkcs11js";
|
||||||
|
|
||||||
import { TEnvConfig } from "@app/lib/config/env";
|
import { TEnvConfig } from "@app/lib/config/env";
|
||||||
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
|
import { KMS_ROOT_CONFIG_UUID } from "@app/services/kms/kms-fns";
|
||||||
|
import { TKmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
|
import { RootKeyEncryptionStrategy } from "@app/services/kms/kms-types";
|
||||||
|
|
||||||
|
import { TLicenseServiceFactory } from "../license/license-service";
|
||||||
|
import { THsmServiceFactory } from "./hsm-service";
|
||||||
import { HsmModule } from "./hsm-types";
|
import { HsmModule } from "./hsm-types";
|
||||||
|
|
||||||
export const initializeHsmModule = (envConfig: Pick<TEnvConfig, "isHsmConfigured" | "HSM_LIB_PATH">) => {
|
export const initializeHsmModule = (envConfig: Pick<TEnvConfig, "isHsmConfigured" | "HSM_LIB_PATH">) => {
|
||||||
@@ -25,10 +31,9 @@ export const initializeHsmModule = (envConfig: Pick<TEnvConfig, "isHsmConfigured
|
|||||||
|
|
||||||
logger.info("PKCS#11 module initialized");
|
logger.info("PKCS#11 module initialized");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error, "Failed to initialize PKCS#11 module");
|
|
||||||
|
|
||||||
if ((error as { message?: string })?.message === "CKR_CRYPTOKI_ALREADY_INITIALIZED") {
|
if ((error as { message?: string })?.message === "CKR_CRYPTOKI_ALREADY_INITIALIZED") {
|
||||||
logger.info("Skipping HSM initialization because it's already initialized.");
|
logger.info("Skipping HSM initialization because it's already initialized.");
|
||||||
|
isInitialized = true;
|
||||||
} else {
|
} else {
|
||||||
logger.error(error, "Failed to initialize PKCS#11 module");
|
logger.error(error, "Failed to initialize PKCS#11 module");
|
||||||
throw error;
|
throw error;
|
||||||
@@ -60,3 +65,36 @@ export const initializeHsmModule = (envConfig: Pick<TEnvConfig, "isHsmConfigured
|
|||||||
getModule
|
getModule
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isHsmActiveAndEnabled = async ({
|
||||||
|
hsmService,
|
||||||
|
kmsRootConfigDAL,
|
||||||
|
licenseService
|
||||||
|
}: {
|
||||||
|
hsmService: Pick<THsmServiceFactory, "isActive">;
|
||||||
|
kmsRootConfigDAL: Pick<TKmsRootConfigDALFactory, "findById">;
|
||||||
|
licenseService?: Pick<TLicenseServiceFactory, "onPremFeatures">;
|
||||||
|
}) => {
|
||||||
|
const isHsmConfigured = await hsmService.isActive();
|
||||||
|
|
||||||
|
// null if the root kms config does not exist
|
||||||
|
let rootKmsConfigEncryptionStrategy: RootKeyEncryptionStrategy | null = null;
|
||||||
|
|
||||||
|
const rootKmsConfig = await kmsRootConfigDAL.findById(KMS_ROOT_CONFIG_UUID).catch(() => null);
|
||||||
|
|
||||||
|
rootKmsConfigEncryptionStrategy = (rootKmsConfig?.encryptionStrategy || null) as RootKeyEncryptionStrategy | null;
|
||||||
|
if (
|
||||||
|
rootKmsConfigEncryptionStrategy === RootKeyEncryptionStrategy.HSM &&
|
||||||
|
licenseService &&
|
||||||
|
!licenseService.onPremFeatures.hsm
|
||||||
|
) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Your license does not include HSM integration. Please upgrade to the Enterprise plan to use HSM."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
rootKmsConfigEncryptionStrategy,
|
||||||
|
isHsmConfigured
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ export const hsmServiceFactory = ({ hsmModule: { isInitialized, pkcs11 }, envCon
|
|||||||
const AES_KEY_SIZE = 256;
|
const AES_KEY_SIZE = 256;
|
||||||
const HMAC_KEY_SIZE = 256;
|
const HMAC_KEY_SIZE = 256;
|
||||||
|
|
||||||
|
let pkcs11TestPassed = false;
|
||||||
|
|
||||||
const $withSession = async <T>(callbackWithSession: SessionCallback<T>): Promise<T> => {
|
const $withSession = async <T>(callbackWithSession: SessionCallback<T>): Promise<T> => {
|
||||||
const RETRY_INTERVAL = 200; // 200ms between attempts
|
const RETRY_INTERVAL = 200; // 200ms between attempts
|
||||||
const MAX_TIMEOUT = 90_000; // 90 seconds maximum total time
|
const MAX_TIMEOUT = 90_000; // 90 seconds maximum total time
|
||||||
@@ -363,7 +365,9 @@ export const hsmServiceFactory = ({ hsmModule: { isInitialized, pkcs11 }, envCon
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let pkcs11TestPassed = false;
|
if (pkcs11TestPassed) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
pkcs11TestPassed = await $withSession($testPkcs11Module);
|
pkcs11TestPassed = await $withSession($testPkcs11Module);
|
||||||
@@ -371,7 +375,7 @@ export const hsmServiceFactory = ({ hsmModule: { isInitialized, pkcs11 }, envCon
|
|||||||
logger.error(err, "HSM: Error testing PKCS#11 module");
|
logger.error(err, "HSM: Error testing PKCS#11 module");
|
||||||
}
|
}
|
||||||
|
|
||||||
return envConfig.isHsmConfigured && isInitialized && pkcs11TestPassed;
|
return pkcs11TestPassed;
|
||||||
};
|
};
|
||||||
|
|
||||||
const startService = async () => {
|
const startService = async () => {
|
||||||
@@ -460,10 +464,23 @@ export const hsmServiceFactory = ({ hsmModule: { isInitialized, pkcs11 }, envCon
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const randomBytes = async (length: number) => {
|
||||||
|
if (!pkcs11 || !isInitialized) {
|
||||||
|
throw new Error("PKCS#11 module is not initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
const randomData = await $withSession((sessionHandle) =>
|
||||||
|
pkcs11.C_GenerateRandom(sessionHandle, Buffer.alloc(length))
|
||||||
|
);
|
||||||
|
|
||||||
|
return randomData;
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
encrypt,
|
encrypt,
|
||||||
startService,
|
startService,
|
||||||
isActive,
|
isActive,
|
||||||
decrypt
|
decrypt,
|
||||||
|
randomBytes
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import pkcs11js from "pkcs11js";
|
import pkcs11js from "pkcs11js";
|
||||||
|
|
||||||
|
import { RootKeyEncryptionStrategy } from "@app/services/kms/kms-types";
|
||||||
|
|
||||||
export type HsmModule = {
|
export type HsmModule = {
|
||||||
pkcs11: pkcs11js.PKCS11;
|
pkcs11: pkcs11js.PKCS11;
|
||||||
isInitialized: boolean;
|
isInitialized: boolean;
|
||||||
@@ -9,3 +11,8 @@ export enum HsmKeyType {
|
|||||||
AES = "AES",
|
AES = "AES",
|
||||||
HMAC = "hmac"
|
HMAC = "hmac"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type THsmStatus = {
|
||||||
|
rootKmsConfigEncryptionStrategy: RootKeyEncryptionStrategy | null;
|
||||||
|
isHsmConfigured: boolean;
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
|
import { OrganizationActionScope } from "@app/db/schemas";
|
||||||
import { EventType, TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType, TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
import {
|
import {
|
||||||
@@ -68,13 +69,14 @@ export const identityAuthTemplateServiceFactory = ({
|
|||||||
templateFields: Record<string, unknown>;
|
templateFields: Record<string, unknown>;
|
||||||
} & Omit<TOrgPermission, "orgId">) => {
|
} & Omit<TOrgPermission, "orgId">) => {
|
||||||
await $checkPlan(actorOrgId);
|
await $checkPlan(actorOrgId);
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionMachineIdentityAuthTemplateActions.CreateTemplates,
|
OrgPermissionMachineIdentityAuthTemplateActions.CreateTemplates,
|
||||||
OrgPermissionSubjects.MachineIdentityAuthTemplate
|
OrgPermissionSubjects.MachineIdentityAuthTemplate
|
||||||
@@ -113,13 +115,14 @@ export const identityAuthTemplateServiceFactory = ({
|
|||||||
throw new NotFoundError({ message: "Template not found" });
|
throw new NotFoundError({ message: "Template not found" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
template.orgId,
|
orgId: template.orgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionMachineIdentityAuthTemplateActions.EditTemplates,
|
OrgPermissionMachineIdentityAuthTemplateActions.EditTemplates,
|
||||||
OrgPermissionSubjects.MachineIdentityAuthTemplate
|
OrgPermissionSubjects.MachineIdentityAuthTemplate
|
||||||
@@ -227,13 +230,14 @@ export const identityAuthTemplateServiceFactory = ({
|
|||||||
throw new NotFoundError({ message: "Template not found" });
|
throw new NotFoundError({ message: "Template not found" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
template.orgId,
|
orgId: template.orgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionMachineIdentityAuthTemplateActions.DeleteTemplates,
|
OrgPermissionMachineIdentityAuthTemplateActions.DeleteTemplates,
|
||||||
OrgPermissionSubjects.MachineIdentityAuthTemplate
|
OrgPermissionSubjects.MachineIdentityAuthTemplate
|
||||||
@@ -282,13 +286,14 @@ export const identityAuthTemplateServiceFactory = ({
|
|||||||
throw new NotFoundError({ message: "Template not found" });
|
throw new NotFoundError({ message: "Template not found" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
template.orgId,
|
orgId: template.orgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionMachineIdentityAuthTemplateActions.ListTemplates,
|
OrgPermissionMachineIdentityAuthTemplateActions.ListTemplates,
|
||||||
OrgPermissionSubjects.MachineIdentityAuthTemplate
|
OrgPermissionSubjects.MachineIdentityAuthTemplate
|
||||||
@@ -316,13 +321,14 @@ export const identityAuthTemplateServiceFactory = ({
|
|||||||
actorOrgId
|
actorOrgId
|
||||||
}: TListIdentityAuthTemplatesDTO) => {
|
}: TListIdentityAuthTemplatesDTO) => {
|
||||||
await $checkPlan(actorOrgId);
|
await $checkPlan(actorOrgId);
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionMachineIdentityAuthTemplateActions.ListTemplates,
|
OrgPermissionMachineIdentityAuthTemplateActions.ListTemplates,
|
||||||
OrgPermissionSubjects.MachineIdentityAuthTemplate
|
OrgPermissionSubjects.MachineIdentityAuthTemplate
|
||||||
@@ -352,13 +358,14 @@ export const identityAuthTemplateServiceFactory = ({
|
|||||||
actorOrgId
|
actorOrgId
|
||||||
}: TGetTemplatesByAuthMethodDTO) => {
|
}: TGetTemplatesByAuthMethodDTO) => {
|
||||||
await $checkPlan(actorOrgId);
|
await $checkPlan(actorOrgId);
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionMachineIdentityAuthTemplateActions.AttachTemplates,
|
OrgPermissionMachineIdentityAuthTemplateActions.AttachTemplates,
|
||||||
OrgPermissionSubjects.MachineIdentityAuthTemplate
|
OrgPermissionSubjects.MachineIdentityAuthTemplate
|
||||||
@@ -385,13 +392,14 @@ export const identityAuthTemplateServiceFactory = ({
|
|||||||
actorOrgId
|
actorOrgId
|
||||||
}: TFindTemplateUsagesDTO) => {
|
}: TFindTemplateUsagesDTO) => {
|
||||||
await $checkPlan(actorOrgId);
|
await $checkPlan(actorOrgId);
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionMachineIdentityAuthTemplateActions.ListTemplates,
|
OrgPermissionMachineIdentityAuthTemplateActions.ListTemplates,
|
||||||
OrgPermissionSubjects.MachineIdentityAuthTemplate
|
OrgPermissionSubjects.MachineIdentityAuthTemplate
|
||||||
@@ -415,13 +423,14 @@ export const identityAuthTemplateServiceFactory = ({
|
|||||||
actorOrgId
|
actorOrgId
|
||||||
}: TUnlinkTemplateUsageDTO) => {
|
}: TUnlinkTemplateUsageDTO) => {
|
||||||
await $checkPlan(actorOrgId);
|
await $checkPlan(actorOrgId);
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionMachineIdentityAuthTemplateActions.UnlinkTemplates,
|
OrgPermissionMachineIdentityAuthTemplateActions.UnlinkTemplates,
|
||||||
OrgPermissionSubjects.MachineIdentityAuthTemplate
|
OrgPermissionSubjects.MachineIdentityAuthTemplate
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
|
import { OrganizationActionScope } from "@app/db/schemas";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { TKmsKeyDALFactory } from "@app/services/kms/kms-key-dal";
|
import { TKmsKeyDALFactory } from "@app/services/kms/kms-key-dal";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
@@ -46,13 +47,14 @@ export const kmipOperationServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
}: TKmipCreateDTO) => {
|
}: TKmipCreateDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
||||||
|
|
||||||
@@ -78,13 +80,14 @@ export const kmipOperationServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const destroy = async ({ projectId, id, clientId, actor, actorId, actorOrgId, actorAuthMethod }: TKmipDestroyDTO) => {
|
const destroy = async ({ projectId, id, clientId, actor, actorId, actorOrgId, actorAuthMethod }: TKmipDestroyDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
||||||
|
|
||||||
@@ -131,13 +134,14 @@ export const kmipOperationServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const get = async ({ projectId, id, clientId, actor, actorId, actorAuthMethod, actorOrgId }: TKmipGetDTO) => {
|
const get = async ({ projectId, id, clientId, actor, actorId, actorAuthMethod, actorOrgId }: TKmipGetDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
||||||
|
|
||||||
@@ -189,13 +193,14 @@ export const kmipOperationServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const activate = async ({ projectId, id, clientId, actor, actorId, actorAuthMethod, actorOrgId }: TKmipGetDTO) => {
|
const activate = async ({ projectId, id, clientId, actor, actorId, actorAuthMethod, actorOrgId }: TKmipGetDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
||||||
|
|
||||||
@@ -226,13 +231,14 @@ export const kmipOperationServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const revoke = async ({ projectId, id, clientId, actor, actorId, actorAuthMethod, actorOrgId }: TKmipRevokeDTO) => {
|
const revoke = async ({ projectId, id, clientId, actor, actorId, actorAuthMethod, actorOrgId }: TKmipRevokeDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
||||||
|
|
||||||
@@ -287,13 +293,14 @@ export const kmipOperationServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
}: TKmipGetAttributesDTO) => {
|
}: TKmipGetAttributesDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
||||||
|
|
||||||
@@ -339,13 +346,14 @@ export const kmipOperationServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const locate = async ({ projectId, clientId, actor, actorId, actorAuthMethod, actorOrgId }: TKmipLocateDTO) => {
|
const locate = async ({ projectId, clientId, actor, actorId, actorAuthMethod, actorOrgId }: TKmipLocateDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
||||||
|
|
||||||
@@ -377,13 +385,14 @@ export const kmipOperationServiceFactory = ({
|
|||||||
actorOrgId,
|
actorOrgId,
|
||||||
kmipMetadata
|
kmipMetadata
|
||||||
}: TKmipRegisterDTO) => {
|
}: TKmipRegisterDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import * as x509 from "@peculiar/x509";
|
import * as x509 from "@peculiar/x509";
|
||||||
|
|
||||||
import { ActionProjectType } from "@app/db/schemas";
|
import { ActionProjectType, OrganizationActionScope } from "@app/db/schemas";
|
||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
import { BadRequestError, InternalServerError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, InternalServerError, NotFoundError } from "@app/lib/errors";
|
||||||
import { isValidIp } from "@app/lib/ip";
|
import { isValidIp } from "@app/lib/ip";
|
||||||
@@ -401,13 +401,14 @@ export const kmipServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const setupOrgKmip = async ({ caKeyAlgorithm, actorOrgId, actor, actorId, actorAuthMethod }: TSetupOrgKmipDTO) => {
|
const setupOrgKmip = async ({ caKeyAlgorithm, actorOrgId, actor, actorId, actorAuthMethod }: TSetupOrgKmipDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Setup, OrgPermissionSubjects.Kmip);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Setup, OrgPermissionSubjects.Kmip);
|
||||||
|
|
||||||
const kmipConfig = await kmipOrgConfigDAL.findOne({
|
const kmipConfig = await kmipOrgConfigDAL.findOne({
|
||||||
@@ -566,7 +567,14 @@ export const kmipServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getOrgKmip = async ({ actorOrgId, actor, actorId, actorAuthMethod }: TGetOrgKmipDTO) => {
|
const getOrgKmip = async ({ actorOrgId, actor, actorId, actorAuthMethod }: TGetOrgKmipDTO) => {
|
||||||
await permissionService.getOrgPermission(actor, actorId, actorOrgId, actorAuthMethod, actorOrgId);
|
await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
orgId: actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
});
|
||||||
|
|
||||||
const kmipConfig = await kmipOrgConfigDAL.findOne({
|
const kmipConfig = await kmipOrgConfigDAL.findOne({
|
||||||
orgId: actorOrgId
|
orgId: actorOrgId
|
||||||
@@ -759,13 +767,14 @@ export const kmipServiceFactory = ({
|
|||||||
keyAlgorithm,
|
keyAlgorithm,
|
||||||
hostnamesOrIps
|
hostnamesOrIps
|
||||||
}: TRegisterServerDTO) => {
|
}: TRegisterServerDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { AccessScope, OrgMembershipStatus, TableName, TLdapConfigsUpdate, TUsers } from "@app/db/schemas";
|
import {
|
||||||
|
AccessScope,
|
||||||
|
OrganizationActionScope,
|
||||||
|
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";
|
||||||
@@ -119,7 +126,14 @@ export const ldapConfigServiceFactory = ({
|
|||||||
groupSearchFilter,
|
groupSearchFilter,
|
||||||
caCert
|
caCert
|
||||||
}: TCreateLdapCfgDTO) => {
|
}: TCreateLdapCfgDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.ParentOrganization,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
orgId: actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Ldap);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Ldap);
|
||||||
|
|
||||||
const plan = await licenseService.getPlan(orgId);
|
const plan = await licenseService.getPlan(orgId);
|
||||||
@@ -238,7 +252,14 @@ export const ldapConfigServiceFactory = ({
|
|||||||
groupSearchFilter,
|
groupSearchFilter,
|
||||||
caCert
|
caCert
|
||||||
}: TUpdateLdapCfgDTO) => {
|
}: TUpdateLdapCfgDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.ParentOrganization,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
orgId: actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Ldap);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Ldap);
|
||||||
|
|
||||||
const plan = await licenseService.getPlan(orgId);
|
const plan = await licenseService.getPlan(orgId);
|
||||||
@@ -316,7 +337,14 @@ export const ldapConfigServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
}: TGetLdapCfgDTO) => {
|
}: TGetLdapCfgDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.ParentOrganization,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
orgId: actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Ldap);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Ldap);
|
||||||
return getLdapCfg({
|
return getLdapCfg({
|
||||||
orgId
|
orgId
|
||||||
@@ -649,7 +677,14 @@ export const ldapConfigServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
}: TGetLdapGroupMapsDTO) => {
|
}: TGetLdapGroupMapsDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.ParentOrganization,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
orgId: actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Ldap);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Ldap);
|
||||||
|
|
||||||
const ldapConfig = await ldapConfigDAL.findOne({
|
const ldapConfig = await ldapConfigDAL.findOne({
|
||||||
@@ -678,7 +713,14 @@ export const ldapConfigServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
}: TCreateLdapGroupMapDTO) => {
|
}: TCreateLdapGroupMapDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.ParentOrganization,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
orgId: actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Ldap);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Ldap);
|
||||||
|
|
||||||
const plan = await licenseService.getPlan(orgId);
|
const plan = await licenseService.getPlan(orgId);
|
||||||
@@ -732,7 +774,14 @@ export const ldapConfigServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
}: TDeleteLdapGroupMapDTO) => {
|
}: TDeleteLdapGroupMapDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.ParentOrganization,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
orgId: actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Ldap);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Ldap);
|
||||||
|
|
||||||
const plan = await licenseService.getPlan(orgId);
|
const plan = await licenseService.getPlan(orgId);
|
||||||
@@ -771,7 +820,14 @@ export const ldapConfigServiceFactory = ({
|
|||||||
caCert,
|
caCert,
|
||||||
url
|
url
|
||||||
}: TTestLdapConnectionDTO) => {
|
}: TTestLdapConnectionDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.ParentOrganization,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
orgId: actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Ldap);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Ldap);
|
||||||
|
|
||||||
const plan = await licenseService.getPlan(orgId);
|
const plan = await licenseService.getPlan(orgId);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export const licenseDALFactory = (db: TDbClient) => {
|
|||||||
const countOfOrgMembers = async (orgId: string | null, tx?: Knex) => {
|
const countOfOrgMembers = async (orgId: string | null, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const doc = await (tx || db.replicaNode())(TableName.Membership)
|
const doc = await (tx || db.replicaNode())(TableName.Membership)
|
||||||
|
.join(TableName.Organization, `${TableName.Organization}.id`, `${TableName.Membership}.scopeOrgId`)
|
||||||
.where({ status: OrgMembershipStatus.Accepted, scope: AccessScope.Organization })
|
.where({ status: OrgMembershipStatus.Accepted, scope: AccessScope.Organization })
|
||||||
.andWhere((bd) => {
|
.andWhere((bd) => {
|
||||||
if (orgId) {
|
if (orgId) {
|
||||||
@@ -18,6 +19,7 @@ export const licenseDALFactory = (db: TDbClient) => {
|
|||||||
})
|
})
|
||||||
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
|
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
|
||||||
.where(`${TableName.Users}.isGhost`, false)
|
.where(`${TableName.Users}.isGhost`, false)
|
||||||
|
.whereNull(`${TableName.Organization}.rootOrgId`)
|
||||||
.count();
|
.count();
|
||||||
return Number(doc?.[0]?.count ?? 0);
|
return Number(doc?.[0]?.count ?? 0);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -25,10 +27,31 @@ export const licenseDALFactory = (db: TDbClient) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const countOfOrgIdentities = async (orgId: string | null, tx?: Knex) => {
|
||||||
|
try {
|
||||||
|
// count org identities
|
||||||
|
const identityDoc = await (tx || db.replicaNode())(TableName.Identity)
|
||||||
|
.join(TableName.Organization, `${TableName.Identity}.orgId`, `${TableName.Organization}.id`)
|
||||||
|
.where((bd) => {
|
||||||
|
if (orgId) {
|
||||||
|
void bd.where(`${TableName.Organization}.rootOrgId`, orgId).orWhere(`${TableName.Organization}.id`, orgId);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.count();
|
||||||
|
|
||||||
|
const identityCount = Number(identityDoc?.[0].count);
|
||||||
|
|
||||||
|
return identityCount;
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "Count of Org Users + Identities" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const countOrgUsersAndIdentities = async (orgId: string | null, tx?: Knex) => {
|
const countOrgUsersAndIdentities = async (orgId: string | null, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
// count org users
|
// count org users
|
||||||
const userDoc = await (tx || db.replicaNode())(TableName.Membership)
|
const userDoc = await (tx || db.replicaNode())(TableName.Membership)
|
||||||
|
.join(TableName.Organization, `${TableName.Organization}.id`, `${TableName.Membership}.scopeOrgId`)
|
||||||
.where({ status: OrgMembershipStatus.Accepted, scope: AccessScope.Organization })
|
.where({ status: OrgMembershipStatus.Accepted, scope: AccessScope.Organization })
|
||||||
.whereNotNull(`${TableName.Membership}.actorUserId`)
|
.whereNotNull(`${TableName.Membership}.actorUserId`)
|
||||||
.andWhere((bd) => {
|
.andWhere((bd) => {
|
||||||
@@ -38,17 +61,17 @@ export const licenseDALFactory = (db: TDbClient) => {
|
|||||||
})
|
})
|
||||||
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
|
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
|
||||||
.where(`${TableName.Users}.isGhost`, false)
|
.where(`${TableName.Users}.isGhost`, false)
|
||||||
|
.whereNull(`${TableName.Organization}.rootOrgId`)
|
||||||
.count();
|
.count();
|
||||||
|
|
||||||
const userCount = Number(userDoc?.[0].count);
|
const userCount = Number(userDoc?.[0].count);
|
||||||
|
|
||||||
// count org identities
|
// count org identities
|
||||||
const identityDoc = await (tx || db.replicaNode())(TableName.Membership)
|
const identityDoc = await (tx || db.replicaNode())(TableName.Identity)
|
||||||
.where({ scope: AccessScope.Organization })
|
.join(TableName.Organization, `${TableName.Identity}.orgId`, `${TableName.Organization}.id`)
|
||||||
.whereNotNull(`${TableName.Membership}.actorIdentityId`)
|
|
||||||
.where((bd) => {
|
.where((bd) => {
|
||||||
if (orgId) {
|
if (orgId) {
|
||||||
void bd.where(`${TableName.Membership}.scopeOrgId`, orgId);
|
void bd.where(`${TableName.Organization}.rootOrgId`, orgId).orWhere(`${TableName.Organization}.id`, orgId);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.count();
|
.count();
|
||||||
@@ -61,5 +84,5 @@ export const licenseDALFactory = (db: TDbClient) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return { countOfOrgMembers, countOrgUsersAndIdentities };
|
return { countOfOrgMembers, countOrgUsersAndIdentities, countOfOrgIdentities };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
|
|||||||
rbac: false,
|
rbac: false,
|
||||||
githubOrgSync: false,
|
githubOrgSync: false,
|
||||||
customRateLimits: false,
|
customRateLimits: false,
|
||||||
|
subOrganization: false,
|
||||||
customAlerts: false,
|
customAlerts: false,
|
||||||
secretAccessInsights: false,
|
secretAccessInsights: false,
|
||||||
auditLogs: false,
|
auditLogs: false,
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ import { AxiosError } from "axios";
|
|||||||
import { CronJob } from "cron";
|
import { CronJob } from "cron";
|
||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { OrganizationActionScope } from "@app/db/schemas";
|
||||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { TEnvConfig } from "@app/lib/config/env";
|
||||||
import { verifyOfflineLicense } from "@app/lib/crypto";
|
import { verifyOfflineLicense } from "@app/lib/crypto";
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { TIdentityOrgDALFactory } from "@app/services/identity/identity-org-dal";
|
|
||||||
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
|
|
||||||
@@ -45,11 +45,14 @@ import {
|
|||||||
} from "./license-types";
|
} from "./license-types";
|
||||||
|
|
||||||
type TLicenseServiceFactoryDep = {
|
type TLicenseServiceFactoryDep = {
|
||||||
orgDAL: Pick<TOrgDALFactory, "findOrgById" | "countAllOrgMembers">;
|
envConfig: Pick<
|
||||||
|
TEnvConfig,
|
||||||
|
"LICENSE_SERVER_URL" | "LICENSE_SERVER_KEY" | "LICENSE_KEY" | "LICENSE_KEY_OFFLINE" | "INTERNAL_REGION" | "SITE_URL"
|
||||||
|
>;
|
||||||
|
orgDAL: Pick<TOrgDALFactory, "findRootOrgDetails" | "countAllOrgMembers" | "findById">;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
licenseDAL: TLicenseDALFactory;
|
licenseDAL: TLicenseDALFactory;
|
||||||
keyStore: Pick<TKeyStoreFactory, "setItemWithExpiry" | "getItem" | "deleteItem">;
|
keyStore: Pick<TKeyStoreFactory, "setItemWithExpiry" | "getItem" | "deleteItem">;
|
||||||
identityOrgMembershipDAL: TIdentityOrgDALFactory;
|
|
||||||
projectDAL: TProjectDALFactory;
|
projectDAL: TProjectDALFactory;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -66,27 +69,26 @@ export const licenseServiceFactory = ({
|
|||||||
permissionService,
|
permissionService,
|
||||||
licenseDAL,
|
licenseDAL,
|
||||||
keyStore,
|
keyStore,
|
||||||
identityOrgMembershipDAL,
|
projectDAL,
|
||||||
projectDAL
|
envConfig
|
||||||
}: TLicenseServiceFactoryDep) => {
|
}: TLicenseServiceFactoryDep) => {
|
||||||
let isValidLicense = false;
|
let isValidLicense = false;
|
||||||
let instanceType = InstanceType.OnPrem;
|
let instanceType = InstanceType.OnPrem;
|
||||||
let onPremFeatures: TFeatureSet = getDefaultOnPremFeatures();
|
let onPremFeatures: TFeatureSet = getDefaultOnPremFeatures();
|
||||||
let selfHostedLicense: TOfflineLicense | null = null;
|
let selfHostedLicense: TOfflineLicense | null = null;
|
||||||
|
|
||||||
const appCfg = getConfig();
|
|
||||||
const licenseServerCloudApi = setupLicenseRequestWithStore(
|
const licenseServerCloudApi = setupLicenseRequestWithStore(
|
||||||
appCfg.LICENSE_SERVER_URL || "",
|
envConfig.LICENSE_SERVER_URL || "",
|
||||||
LICENSE_SERVER_CLOUD_LOGIN,
|
LICENSE_SERVER_CLOUD_LOGIN,
|
||||||
appCfg.LICENSE_SERVER_KEY || "",
|
envConfig.LICENSE_SERVER_KEY || "",
|
||||||
appCfg.INTERNAL_REGION
|
envConfig.INTERNAL_REGION
|
||||||
);
|
);
|
||||||
|
|
||||||
const licenseServerOnPremApi = setupLicenseRequestWithStore(
|
const licenseServerOnPremApi = setupLicenseRequestWithStore(
|
||||||
appCfg.LICENSE_SERVER_URL || "",
|
envConfig.LICENSE_SERVER_URL || "",
|
||||||
LICENSE_SERVER_ON_PREM_LOGIN,
|
LICENSE_SERVER_ON_PREM_LOGIN,
|
||||||
appCfg.LICENSE_KEY || "",
|
envConfig.LICENSE_KEY || "",
|
||||||
appCfg.INTERNAL_REGION
|
envConfig.INTERNAL_REGION
|
||||||
);
|
);
|
||||||
|
|
||||||
const syncLicenseKeyOnPremFeatures = async (shouldThrow: boolean = false) => {
|
const syncLicenseKeyOnPremFeatures = async (shouldThrow: boolean = false) => {
|
||||||
@@ -120,7 +122,7 @@ export const licenseServiceFactory = ({
|
|||||||
|
|
||||||
const init = async () => {
|
const init = async () => {
|
||||||
try {
|
try {
|
||||||
if (appCfg.LICENSE_SERVER_KEY) {
|
if (envConfig.LICENSE_SERVER_KEY) {
|
||||||
const token = await licenseServerCloudApi.refreshLicense();
|
const token = await licenseServerCloudApi.refreshLicense();
|
||||||
if (token) instanceType = InstanceType.Cloud;
|
if (token) instanceType = InstanceType.Cloud;
|
||||||
logger.info(`Instance type: ${InstanceType.Cloud}`);
|
logger.info(`Instance type: ${InstanceType.Cloud}`);
|
||||||
@@ -128,7 +130,7 @@ export const licenseServiceFactory = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appCfg.LICENSE_KEY) {
|
if (envConfig.LICENSE_KEY) {
|
||||||
const token = await licenseServerOnPremApi.refreshLicense();
|
const token = await licenseServerOnPremApi.refreshLicense();
|
||||||
if (token) {
|
if (token) {
|
||||||
await syncLicenseKeyOnPremFeatures(true);
|
await syncLicenseKeyOnPremFeatures(true);
|
||||||
@@ -139,10 +141,10 @@ export const licenseServiceFactory = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appCfg.LICENSE_KEY_OFFLINE) {
|
if (envConfig.LICENSE_KEY_OFFLINE) {
|
||||||
let isValidOfflineLicense = true;
|
let isValidOfflineLicense = true;
|
||||||
const contents: TOfflineLicenseContents = JSON.parse(
|
const contents: TOfflineLicenseContents = JSON.parse(
|
||||||
Buffer.from(appCfg.LICENSE_KEY_OFFLINE, "base64").toString("utf8")
|
Buffer.from(envConfig.LICENSE_KEY_OFFLINE, "base64").toString("utf8")
|
||||||
);
|
);
|
||||||
const isVerified = await verifyOfflineLicense(JSON.stringify(contents.license), contents.signature);
|
const isVerified = await verifyOfflineLicense(JSON.stringify(contents.license), contents.signature);
|
||||||
|
|
||||||
@@ -181,7 +183,7 @@ export const licenseServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const initializeBackgroundSync = async () => {
|
const initializeBackgroundSync = async () => {
|
||||||
if (appCfg.LICENSE_KEY) {
|
if (envConfig.LICENSE_KEY) {
|
||||||
logger.info("Setting up background sync process for refresh onPremFeatures");
|
logger.info("Setting up background sync process for refresh onPremFeatures");
|
||||||
const job = new CronJob("*/10 * * * *", syncLicenseKeyOnPremFeatures);
|
const job = new CronJob("*/10 * * * *", syncLicenseKeyOnPremFeatures);
|
||||||
job.start();
|
job.start();
|
||||||
@@ -199,22 +201,23 @@ export const licenseServiceFactory = ({
|
|||||||
return JSON.parse(cachedPlan) as TFeatureSet;
|
return JSON.parse(cachedPlan) as TFeatureSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
const org = await orgDAL.findOrgById(orgId);
|
const org = await orgDAL.findRootOrgDetails(orgId);
|
||||||
if (!org) throw new NotFoundError({ message: `Organization with ID '${orgId}' not found` });
|
if (!org) throw new NotFoundError({ message: `Organization with ID '${orgId}' not found` });
|
||||||
|
const rootOrgId = org.id;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: { currentPlan }
|
data: { currentPlan }
|
||||||
} = await licenseServerCloudApi.request.get<{ currentPlan: TFeatureSet }>(
|
} = await licenseServerCloudApi.request.get<{ currentPlan: TFeatureSet }>(
|
||||||
`/api/license-server/v1/customers/${org.customerId}/cloud-plan`
|
`/api/license-server/v1/customers/${org.customerId}/cloud-plan`
|
||||||
);
|
);
|
||||||
const workspacesUsed = await projectDAL.countOfOrgProjects(orgId);
|
const workspacesUsed = await projectDAL.countOfOrgProjects(rootOrgId);
|
||||||
currentPlan.workspacesUsed = workspacesUsed;
|
currentPlan.workspacesUsed = workspacesUsed;
|
||||||
|
|
||||||
const membersUsed = await licenseDAL.countOfOrgMembers(orgId);
|
const membersUsed = await licenseDAL.countOfOrgMembers(rootOrgId);
|
||||||
currentPlan.membersUsed = membersUsed;
|
currentPlan.membersUsed = membersUsed;
|
||||||
const identityUsed = await licenseDAL.countOrgUsersAndIdentities(orgId);
|
const identityUsed = await licenseDAL.countOrgUsersAndIdentities(rootOrgId);
|
||||||
currentPlan.identitiesUsed = identityUsed;
|
|
||||||
|
|
||||||
if (currentPlan.identityLimit && currentPlan.identityLimit !== identityUsed) {
|
if (currentPlan?.identitiesUsed && currentPlan.identitiesUsed !== identityUsed) {
|
||||||
try {
|
try {
|
||||||
await licenseServerCloudApi.request.patch(`/api/license-server/v1/customers/${org.customerId}/cloud-plan`, {
|
await licenseServerCloudApi.request.patch(`/api/license-server/v1/customers/${org.customerId}/cloud-plan`, {
|
||||||
quantity: membersUsed,
|
quantity: membersUsed,
|
||||||
@@ -227,6 +230,7 @@ export const licenseServiceFactory = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
currentPlan.identitiesUsed = identityUsed;
|
||||||
|
|
||||||
await keyStore.setItemWithExpiry(
|
await keyStore.setItemWithExpiry(
|
||||||
FEATURE_CACHE_KEY(org.id),
|
FEATURE_CACHE_KEY(org.id),
|
||||||
@@ -284,19 +288,20 @@ export const licenseServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateSubscriptionOrgMemberCount = async (orgId: string, tx?: Knex) => {
|
const updateSubscriptionOrgMemberCount = async (orgId: string, tx?: Knex) => {
|
||||||
if (instanceType === InstanceType.Cloud) {
|
const org = await orgDAL.findRootOrgDetails(orgId, tx);
|
||||||
const org = await orgDAL.findOrgById(orgId);
|
|
||||||
if (!org) throw new NotFoundError({ message: `Organization with ID '${orgId}' not found` });
|
if (!org) throw new NotFoundError({ message: `Organization with ID '${orgId}' not found` });
|
||||||
|
|
||||||
const quantity = await licenseDAL.countOfOrgMembers(orgId, tx);
|
const rootOrgId = org.id;
|
||||||
const quantityIdentities = await licenseDAL.countOrgUsersAndIdentities(orgId, tx);
|
if (instanceType === InstanceType.Cloud) {
|
||||||
|
const quantity = await licenseDAL.countOfOrgMembers(rootOrgId, tx);
|
||||||
|
const quantityIdentities = await licenseDAL.countOrgUsersAndIdentities(rootOrgId, tx);
|
||||||
if (org?.customerId) {
|
if (org?.customerId) {
|
||||||
await licenseServerCloudApi.request.patch(`/api/license-server/v1/customers/${org.customerId}/cloud-plan`, {
|
await licenseServerCloudApi.request.patch(`/api/license-server/v1/customers/${org.customerId}/cloud-plan`, {
|
||||||
quantity,
|
quantity,
|
||||||
quantityIdentities
|
quantityIdentities
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await keyStore.deleteItem(FEATURE_CACHE_KEY(orgId));
|
await keyStore.deleteItem(FEATURE_CACHE_KEY(rootOrgId));
|
||||||
} else if (instanceType === InstanceType.EnterpriseOnPrem) {
|
} else if (instanceType === InstanceType.EnterpriseOnPrem) {
|
||||||
const usedSeats = await licenseDAL.countOfOrgMembers(null, tx);
|
const usedSeats = await licenseDAL.countOfOrgMembers(null, tx);
|
||||||
const usedIdentitySeats = await licenseDAL.countOrgUsersAndIdentities(null, tx);
|
const usedIdentitySeats = await licenseDAL.countOrgUsersAndIdentities(null, tx);
|
||||||
@@ -307,7 +312,7 @@ export const licenseServiceFactory = ({
|
|||||||
usedIdentitySeats
|
usedIdentitySeats
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await refreshPlan(orgId);
|
await refreshPlan(rootOrgId);
|
||||||
};
|
};
|
||||||
|
|
||||||
// below all are api calls
|
// below all are api calls
|
||||||
@@ -319,7 +324,14 @@ export const licenseServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
billingCycle
|
billingCycle
|
||||||
}: TOrgPlansTableDTO) => {
|
}: TOrgPlansTableDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
actorId,
|
||||||
|
actor,
|
||||||
|
orgId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
scope: OrganizationActionScope.ParentOrganization
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||||
const { data } = await licenseServerCloudApi.request.get(
|
const { data } = await licenseServerCloudApi.request.get(
|
||||||
`/api/license-server/v1/cloud-products?billing-cycle=${billingCycle}`
|
`/api/license-server/v1/cloud-products?billing-cycle=${billingCycle}`
|
||||||
@@ -336,7 +348,14 @@ export const licenseServiceFactory = ({
|
|||||||
projectId,
|
projectId,
|
||||||
refreshCache
|
refreshCache
|
||||||
}: TOrgPlanDTO) => {
|
}: TOrgPlanDTO) => {
|
||||||
await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
await permissionService.getOrgPermission({
|
||||||
|
actorId,
|
||||||
|
actor,
|
||||||
|
orgId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
scope: OrganizationActionScope.ParentOrganization
|
||||||
|
});
|
||||||
if (refreshCache) {
|
if (refreshCache) {
|
||||||
await refreshPlan(orgId);
|
await refreshPlan(orgId);
|
||||||
}
|
}
|
||||||
@@ -352,13 +371,20 @@ export const licenseServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
success_url
|
success_url
|
||||||
}: TStartOrgTrialDTO) => {
|
}: TStartOrgTrialDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
actorId,
|
||||||
|
actor,
|
||||||
|
orgId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
scope: OrganizationActionScope.ParentOrganization
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionBillingActions.ManageBilling,
|
OrgPermissionBillingActions.ManageBilling,
|
||||||
OrgPermissionSubjects.Billing
|
OrgPermissionSubjects.Billing
|
||||||
);
|
);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -384,13 +410,20 @@ export const licenseServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
}: TCreateOrgPortalSession) => {
|
}: TCreateOrgPortalSession) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
actorId,
|
||||||
|
actor,
|
||||||
|
orgId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
scope: OrganizationActionScope.ParentOrganization
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionBillingActions.ManageBilling,
|
OrgPermissionBillingActions.ManageBilling,
|
||||||
OrgPermissionSubjects.Billing
|
OrgPermissionSubjects.Billing
|
||||||
);
|
);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: "Organization not found"
|
message: "Organization not found"
|
||||||
@@ -411,8 +444,8 @@ export const licenseServiceFactory = ({
|
|||||||
} = await licenseServerCloudApi.request.post(
|
} = await licenseServerCloudApi.request.post(
|
||||||
`/api/license-server/v1/customers/${organization.customerId}/billing-details/payment-methods`,
|
`/api/license-server/v1/customers/${organization.customerId}/billing-details/payment-methods`,
|
||||||
{
|
{
|
||||||
success_url: `${appCfg.SITE_URL}/organization/billing`,
|
success_url: `${envConfig.SITE_URL}/organization/billing`,
|
||||||
cancel_url: `${appCfg.SITE_URL}/organization/billing`
|
cancel_url: `${envConfig.SITE_URL}/organization/billing`
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -425,7 +458,7 @@ export const licenseServiceFactory = ({
|
|||||||
} = await licenseServerCloudApi.request.post(
|
} = await licenseServerCloudApi.request.post(
|
||||||
`/api/license-server/v1/customers/${organization.customerId}/billing-details/billing-portal`,
|
`/api/license-server/v1/customers/${organization.customerId}/billing-details/billing-portal`,
|
||||||
{
|
{
|
||||||
return_url: `${appCfg.SITE_URL}/organization/billing`
|
return_url: `${envConfig.SITE_URL}/organization/billing`
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -433,10 +466,17 @@ export const licenseServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getOrgBillingInfo = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
|
const getOrgBillingInfo = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
actorId,
|
||||||
|
actor,
|
||||||
|
orgId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
scope: OrganizationActionScope.ParentOrganization
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -502,7 +542,7 @@ export const licenseServiceFactory = ({
|
|||||||
const getUsageMetrics = async (orgId: string) => {
|
const getUsageMetrics = async (orgId: string) => {
|
||||||
const [orgMembersUsed, identityUsed, projectCount] = await Promise.all([
|
const [orgMembersUsed, identityUsed, projectCount] = await Promise.all([
|
||||||
orgDAL.countAllOrgMembers(orgId),
|
orgDAL.countAllOrgMembers(orgId),
|
||||||
identityOrgMembershipDAL.countAllOrgIdentities({ scopeOrgId: orgId }),
|
licenseDAL.countOfOrgIdentities(orgId),
|
||||||
projectDAL.countOfOrgProjects(orgId)
|
projectDAL.countOfOrgProjects(orgId)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -516,10 +556,17 @@ export const licenseServiceFactory = ({
|
|||||||
|
|
||||||
// returns org current plan feature table
|
// returns org current plan feature table
|
||||||
const getOrgPlanTable = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
|
const getOrgPlanTable = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
actorId,
|
||||||
|
actor,
|
||||||
|
orgId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
scope: OrganizationActionScope.ParentOrganization
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -553,10 +600,17 @@ export const licenseServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getOrgBillingDetails = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
|
const getOrgBillingDetails = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
actorId,
|
||||||
|
actor,
|
||||||
|
orgId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
scope: OrganizationActionScope.ParentOrganization
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -578,13 +632,20 @@ export const licenseServiceFactory = ({
|
|||||||
name,
|
name,
|
||||||
email
|
email
|
||||||
}: TUpdateOrgBillingDetailsDTO) => {
|
}: TUpdateOrgBillingDetailsDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
actorId,
|
||||||
|
actor,
|
||||||
|
orgId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
scope: OrganizationActionScope.ParentOrganization
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionBillingActions.ManageBilling,
|
OrgPermissionBillingActions.ManageBilling,
|
||||||
OrgPermissionSubjects.Billing
|
OrgPermissionSubjects.Billing
|
||||||
);
|
);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -601,10 +662,17 @@ export const licenseServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getOrgPmtMethods = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TOrgPmtMethodsDTO) => {
|
const getOrgPmtMethods = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TOrgPmtMethodsDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
actorId,
|
||||||
|
actor,
|
||||||
|
orgId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
scope: OrganizationActionScope.ParentOrganization
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -628,13 +696,20 @@ export const licenseServiceFactory = ({
|
|||||||
success_url,
|
success_url,
|
||||||
cancel_url
|
cancel_url
|
||||||
}: TAddOrgPmtMethodDTO) => {
|
}: TAddOrgPmtMethodDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
actorId,
|
||||||
|
actor,
|
||||||
|
orgId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
scope: OrganizationActionScope.ParentOrganization
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionBillingActions.ManageBilling,
|
OrgPermissionBillingActions.ManageBilling,
|
||||||
OrgPermissionSubjects.Billing
|
OrgPermissionSubjects.Billing
|
||||||
);
|
);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -660,13 +735,20 @@ export const licenseServiceFactory = ({
|
|||||||
orgId,
|
orgId,
|
||||||
pmtMethodId
|
pmtMethodId
|
||||||
}: TDelOrgPmtMethodDTO) => {
|
}: TDelOrgPmtMethodDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
actorId,
|
||||||
|
actor,
|
||||||
|
orgId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
scope: OrganizationActionScope.ParentOrganization
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionBillingActions.ManageBilling,
|
OrgPermissionBillingActions.ManageBilling,
|
||||||
OrgPermissionSubjects.Billing
|
OrgPermissionSubjects.Billing
|
||||||
);
|
);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -692,10 +774,17 @@ export const licenseServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getOrgTaxIds = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgTaxIdDTO) => {
|
const getOrgTaxIds = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgTaxIdDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
actorId,
|
||||||
|
actor,
|
||||||
|
orgId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
scope: OrganizationActionScope.ParentOrganization
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -710,13 +799,20 @@ export const licenseServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const addOrgTaxId = async ({ actorId, actor, actorAuthMethod, actorOrgId, orgId, type, value }: TAddOrgTaxIdDTO) => {
|
const addOrgTaxId = async ({ actorId, actor, actorAuthMethod, actorOrgId, orgId, type, value }: TAddOrgTaxIdDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
actorId,
|
||||||
|
actor,
|
||||||
|
orgId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
scope: OrganizationActionScope.ParentOrganization
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionBillingActions.ManageBilling,
|
OrgPermissionBillingActions.ManageBilling,
|
||||||
OrgPermissionSubjects.Billing
|
OrgPermissionSubjects.Billing
|
||||||
);
|
);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -734,13 +830,20 @@ export const licenseServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const delOrgTaxId = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId, taxId }: TDelOrgTaxIdDTO) => {
|
const delOrgTaxId = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId, taxId }: TDelOrgTaxIdDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
actorId,
|
||||||
|
actor,
|
||||||
|
orgId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
scope: OrganizationActionScope.ParentOrganization
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionBillingActions.ManageBilling,
|
OrgPermissionBillingActions.ManageBilling,
|
||||||
OrgPermissionSubjects.Billing
|
OrgPermissionSubjects.Billing
|
||||||
);
|
);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -754,10 +857,17 @@ export const licenseServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getOrgTaxInvoices = async ({ actorId, actor, actorOrgId, actorAuthMethod, orgId }: TOrgInvoiceDTO) => {
|
const getOrgTaxInvoices = async ({ actorId, actor, actorOrgId, actorAuthMethod, orgId }: TOrgInvoiceDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
actorId,
|
||||||
|
actor,
|
||||||
|
orgId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
scope: OrganizationActionScope.ParentOrganization
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -771,10 +881,17 @@ export const licenseServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getOrgLicenses = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TOrgLicensesDTO) => {
|
const getOrgLicenses = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TOrgLicensesDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
actorId,
|
||||||
|
actor,
|
||||||
|
orgId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
scope: OrganizationActionScope.ParentOrganization
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing);
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findById(orgId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
@@ -819,7 +936,6 @@ export const licenseServiceFactory = ({
|
|||||||
getLicenseId,
|
getLicenseId,
|
||||||
invalidateGetPlan,
|
invalidateGetPlan,
|
||||||
updateSubscriptionOrgMemberCount,
|
updateSubscriptionOrgMemberCount,
|
||||||
refreshPlan,
|
|
||||||
getOrgPlan,
|
getOrgPlan,
|
||||||
getOrgPlansTableByBillCycle,
|
getOrgPlansTableByBillCycle,
|
||||||
startOrgTrial,
|
startOrgTrial,
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export type TFeatureSet = {
|
|||||||
membersUsed: number;
|
membersUsed: number;
|
||||||
identityLimit: null;
|
identityLimit: null;
|
||||||
identitiesUsed: number;
|
identitiesUsed: number;
|
||||||
|
subOrganization: false;
|
||||||
environmentLimit: null;
|
environmentLimit: null;
|
||||||
environmentsUsed: 0;
|
environmentsUsed: 0;
|
||||||
secretVersioning: true;
|
secretVersioning: true;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
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 { AccessScope, OrgMembershipStatus, TableName, TUsers } from "@app/db/schemas";
|
import { AccessScope, OrganizationActionScope, OrgMembershipStatus, TableName, TUsers } from "@app/db/schemas";
|
||||||
import { TOidcConfigsUpdate } from "@app/db/schemas/oidc-configs";
|
import { TOidcConfigsUpdate } from "@app/db/schemas/oidc-configs";
|
||||||
import { EventType, TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType, TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
|
import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
|
||||||
@@ -118,13 +118,14 @@ export const oidcConfigServiceFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (dto.type === "external") {
|
if (dto.type === "external") {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
dto.actor,
|
actorId: dto.actorId,
|
||||||
dto.actorId,
|
actor: dto.actor,
|
||||||
dto.organizationId,
|
orgId: dto.organizationId,
|
||||||
dto.actorAuthMethod,
|
actorOrgId: dto.actorOrgId,
|
||||||
dto.actorOrgId
|
actorAuthMethod: dto.actorAuthMethod,
|
||||||
);
|
scope: OrganizationActionScope.ParentOrganization
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Sso);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Sso);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -508,13 +509,14 @@ export const oidcConfigServiceFactory = ({
|
|||||||
"Failed to update OIDC SSO configuration due to plan restriction. Upgrade plan to update SSO configuration."
|
"Failed to update OIDC SSO configuration due to plan restriction. Upgrade plan to update SSO configuration."
|
||||||
});
|
});
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
actor,
|
|
||||||
actorId,
|
actorId,
|
||||||
org.id,
|
actor,
|
||||||
|
orgId: org.id,
|
||||||
|
actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
scope: OrganizationActionScope.ParentOrganization
|
||||||
);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Sso);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Sso);
|
||||||
|
|
||||||
if (org.googleSsoAuthEnforced && isActive) {
|
if (org.googleSsoAuthEnforced && isActive) {
|
||||||
@@ -602,13 +604,14 @@ export const oidcConfigServiceFactory = ({
|
|||||||
"Failed to create OIDC SSO configuration due to plan restriction. Upgrade plan to update SSO configuration."
|
"Failed to create OIDC SSO configuration due to plan restriction. Upgrade plan to update SSO configuration."
|
||||||
});
|
});
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
actor,
|
|
||||||
actorId,
|
actorId,
|
||||||
org.id,
|
actor,
|
||||||
|
orgId: org.id,
|
||||||
|
actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
scope: OrganizationActionScope.ParentOrganization
|
||||||
);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Sso);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Sso);
|
||||||
|
|
||||||
if (org.googleSsoAuthEnforced && isActive) {
|
if (org.googleSsoAuthEnforced && isActive) {
|
||||||
@@ -764,7 +767,14 @@ export const oidcConfigServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const isOidcManageGroupMembershipsEnabled = async (orgId: string, actor: OrgServiceActor) => {
|
const isOidcManageGroupMembershipsEnabled = async (orgId: string, actor: OrgServiceActor) => {
|
||||||
await permissionService.getOrgPermission(ActorType.USER, actor.id, orgId, actor.authMethod, actor.orgId);
|
await permissionService.getOrgPermission({
|
||||||
|
actor: ActorType.USER,
|
||||||
|
actorId: actor.id,
|
||||||
|
orgId,
|
||||||
|
actorAuthMethod: actor.authMethod,
|
||||||
|
actorOrgId: actor.orgId,
|
||||||
|
scope: OrganizationActionScope.ParentOrganization
|
||||||
|
});
|
||||||
|
|
||||||
const oidcConfig = await oidcConfigDAL.findOne({
|
const oidcConfig = await oidcConfigDAL.findOne({
|
||||||
orgId,
|
orgId,
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ export const pamAccountDALFactory = (db: TDbClient) => {
|
|||||||
.select(
|
.select(
|
||||||
// resource
|
// resource
|
||||||
db.ref("name").withSchema(TableName.PamResource).as("resourceName"),
|
db.ref("name").withSchema(TableName.PamResource).as("resourceName"),
|
||||||
db.ref("resourceType").withSchema(TableName.PamResource)
|
db.ref("resourceType").withSchema(TableName.PamResource),
|
||||||
|
db.ref("encryptedRotationAccountCredentials").withSchema(TableName.PamResource)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (filter) {
|
if (filter) {
|
||||||
@@ -28,16 +29,35 @@ export const pamAccountDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const accounts = await query;
|
const accounts = await query;
|
||||||
|
|
||||||
return accounts.map(({ resourceId, resourceName, resourceType, ...account }) => ({
|
return accounts.map(
|
||||||
|
({ resourceId, resourceName, resourceType, encryptedRotationAccountCredentials, ...account }) => ({
|
||||||
...account,
|
...account,
|
||||||
resourceId,
|
resourceId,
|
||||||
resource: {
|
resource: {
|
||||||
id: resourceId,
|
id: resourceId,
|
||||||
name: resourceName,
|
name: resourceName,
|
||||||
resourceType
|
resourceType,
|
||||||
|
encryptedRotationAccountCredentials
|
||||||
}
|
}
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return { ...orm, findWithResourceDetails };
|
const findAccountsDueForRotation = async (tx?: Knex) => {
|
||||||
|
const dbClient = tx || db.replicaNode();
|
||||||
|
|
||||||
|
const accounts = await dbClient(TableName.PamAccount)
|
||||||
|
.innerJoin(TableName.PamResource, `${TableName.PamAccount}.resourceId`, `${TableName.PamResource}.id`)
|
||||||
|
.whereNotNull(`${TableName.PamResource}.encryptedRotationAccountCredentials`)
|
||||||
|
.whereNotNull(`${TableName.PamAccount}.rotationIntervalSeconds`)
|
||||||
|
.where(`${TableName.PamAccount}.rotationEnabled`, true)
|
||||||
|
.whereRaw(
|
||||||
|
`COALESCE("${TableName.PamAccount}"."lastRotatedAt", "${TableName.PamAccount}"."createdAt") + "${TableName.PamAccount}"."rotationIntervalSeconds" * interval '1 second' < NOW()`
|
||||||
|
)
|
||||||
|
.select(selectAllTableCols(TableName.PamAccount));
|
||||||
|
|
||||||
|
return accounts;
|
||||||
|
};
|
||||||
|
|
||||||
|
return { ...orm, findWithResourceDetails, findAccountsDueForRotation };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ForbiddenError, subject } from "@casl/ability";
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
|
|
||||||
import { ActionProjectType, TPamAccounts, TPamResources } from "@app/db/schemas";
|
import { ActionProjectType, OrganizationActionScope, TPamAccounts, TPamResources } from "@app/db/schemas";
|
||||||
import { PAM_RESOURCE_FACTORY_MAP } from "@app/ee/services/pam-resource/pam-resource-factory";
|
import { PAM_RESOURCE_FACTORY_MAP } from "@app/ee/services/pam-resource/pam-resource-factory";
|
||||||
import { decryptResource, decryptResourceConnectionDetails } from "@app/ee/services/pam-resource/pam-resource-fns";
|
import { decryptResource, decryptResourceConnectionDetails } from "@app/ee/services/pam-resource/pam-resource-fns";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
@@ -11,12 +11,14 @@ import {
|
|||||||
} from "@app/ee/services/permission/project-permission";
|
} from "@app/ee/services/permission/project-permission";
|
||||||
import { DatabaseErrorCode } from "@app/lib/error-codes";
|
import { DatabaseErrorCode } from "@app/lib/error-codes";
|
||||||
import { BadRequestError, DatabaseError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, DatabaseError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
|
import { logger } from "@app/lib/logger";
|
||||||
import { OrgServiceActor } from "@app/lib/types";
|
import { OrgServiceActor } from "@app/lib/types";
|
||||||
import { ActorType } from "@app/services/auth/auth-type";
|
import { ActorType } from "@app/services/auth/auth-type";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||||
|
|
||||||
|
import { EventType, TAuditLogServiceFactory } from "../audit-log/audit-log-types";
|
||||||
import { TGatewayV2ServiceFactory } from "../gateway-v2/gateway-v2-service";
|
import { TGatewayV2ServiceFactory } from "../gateway-v2/gateway-v2-service";
|
||||||
import { TLicenseServiceFactory } from "../license/license-service";
|
import { TLicenseServiceFactory } from "../license/license-service";
|
||||||
import { TPamFolderDALFactory } from "../pam-folder/pam-folder-dal";
|
import { TPamFolderDALFactory } from "../pam-folder/pam-folder-dal";
|
||||||
@@ -45,10 +47,12 @@ type TPamAccountServiceFactoryDep = {
|
|||||||
"getPAMConnectionDetails" | "getPlatformConnectionDetailsByGatewayId"
|
"getPAMConnectionDetails" | "getPlatformConnectionDetailsByGatewayId"
|
||||||
>;
|
>;
|
||||||
userDAL: TUserDALFactory;
|
userDAL: TUserDALFactory;
|
||||||
|
auditLogService: Pick<TAuditLogServiceFactory, "createAuditLog">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TPamAccountServiceFactory = ReturnType<typeof pamAccountServiceFactory>;
|
export type TPamAccountServiceFactory = ReturnType<typeof pamAccountServiceFactory>;
|
||||||
|
|
||||||
|
const ROTATION_CONCURRENCY_LIMIT = 10;
|
||||||
|
|
||||||
export const pamAccountServiceFactory = ({
|
export const pamAccountServiceFactory = ({
|
||||||
pamResourceDAL,
|
pamResourceDAL,
|
||||||
pamSessionDAL,
|
pamSessionDAL,
|
||||||
@@ -59,10 +63,19 @@ export const pamAccountServiceFactory = ({
|
|||||||
permissionService,
|
permissionService,
|
||||||
licenseService,
|
licenseService,
|
||||||
kmsService,
|
kmsService,
|
||||||
gatewayV2Service
|
gatewayV2Service,
|
||||||
|
auditLogService
|
||||||
}: TPamAccountServiceFactoryDep) => {
|
}: TPamAccountServiceFactoryDep) => {
|
||||||
const create = async (
|
const create = async (
|
||||||
{ credentials, resourceId, name, description, folderId }: TCreateAccountDTO,
|
{
|
||||||
|
credentials,
|
||||||
|
resourceId,
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
folderId,
|
||||||
|
rotationEnabled,
|
||||||
|
rotationIntervalSeconds
|
||||||
|
}: TCreateAccountDTO,
|
||||||
actor: OrgServiceActor
|
actor: OrgServiceActor
|
||||||
) => {
|
) => {
|
||||||
const orgLicensePlan = await licenseService.getPlan(actor.orgId);
|
const orgLicensePlan = await licenseService.getPlan(actor.orgId);
|
||||||
@@ -72,6 +85,12 @@ export const pamAccountServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rotationEnabled && (rotationIntervalSeconds === undefined || rotationIntervalSeconds === null)) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Rotation interval must be defined when rotation is enabled."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const resource = await pamResourceDAL.findById(resourceId);
|
const resource = await pamResourceDAL.findById(resourceId);
|
||||||
if (!resource) throw new NotFoundError({ message: `Resource with ID '${resourceId}' not found` });
|
if (!resource) throw new NotFoundError({ message: `Resource with ID '${resourceId}' not found` });
|
||||||
|
|
||||||
@@ -84,6 +103,10 @@ export const pamAccountServiceFactory = ({
|
|||||||
actionProjectType: ActionProjectType.PAM
|
actionProjectType: ActionProjectType.PAM
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!resource.encryptedRotationAccountCredentials && rotationEnabled) {
|
||||||
|
throw new NotFoundError({ message: "Rotation credentials are not configured for this account's resource" });
|
||||||
|
}
|
||||||
|
|
||||||
const accountPath = await getFullPamFolderPath({
|
const accountPath = await getFullPamFolderPath({
|
||||||
pamFolderDAL,
|
pamFolderDAL,
|
||||||
folderId,
|
folderId,
|
||||||
@@ -126,12 +149,19 @@ export const pamAccountServiceFactory = ({
|
|||||||
encryptedCredentials,
|
encryptedCredentials,
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
folderId
|
folderId,
|
||||||
|
rotationEnabled,
|
||||||
|
rotationIntervalSeconds
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...(await decryptAccount(account, resource.projectId, kmsService)),
|
...(await decryptAccount(account, resource.projectId, kmsService)),
|
||||||
resource: { id: resource.id, name: resource.name, resourceType: resource.resourceType }
|
resource: {
|
||||||
|
id: resource.id,
|
||||||
|
name: resource.name,
|
||||||
|
resourceType: resource.resourceType,
|
||||||
|
rotationCredentialsConfigured: !!resource.encryptedRotationAccountCredentials
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof DatabaseError && (err.error as { code: string })?.code === DatabaseErrorCode.UniqueViolation) {
|
if (err instanceof DatabaseError && (err.error as { code: string })?.code === DatabaseErrorCode.UniqueViolation) {
|
||||||
@@ -145,7 +175,7 @@ export const pamAccountServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateById = async (
|
const updateById = async (
|
||||||
{ accountId, credentials, description, name }: TUpdateAccountDTO,
|
{ accountId, credentials, description, name, rotationEnabled, rotationIntervalSeconds }: TUpdateAccountDTO,
|
||||||
actor: OrgServiceActor
|
actor: OrgServiceActor
|
||||||
) => {
|
) => {
|
||||||
const orgLicensePlan = await licenseService.getPlan(actor.orgId);
|
const orgLicensePlan = await licenseService.getPlan(actor.orgId);
|
||||||
@@ -195,6 +225,17 @@ export const pamAccountServiceFactory = ({
|
|||||||
updateDoc.description = description;
|
updateDoc.description = description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rotationEnabled !== undefined) {
|
||||||
|
if (!resource.encryptedRotationAccountCredentials && rotationEnabled) {
|
||||||
|
throw new NotFoundError({ message: "Rotation credentials are not configured for this account's resource" });
|
||||||
|
}
|
||||||
|
updateDoc.rotationEnabled = rotationEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rotationIntervalSeconds !== undefined) {
|
||||||
|
updateDoc.rotationIntervalSeconds = rotationIntervalSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
if (credentials !== undefined) {
|
if (credentials !== undefined) {
|
||||||
const connectionDetails = await decryptResourceConnectionDetails({
|
const connectionDetails = await decryptResourceConnectionDetails({
|
||||||
projectId: account.projectId,
|
projectId: account.projectId,
|
||||||
@@ -211,7 +252,7 @@ export const pamAccountServiceFactory = ({
|
|||||||
|
|
||||||
// Logic to prevent overwriting unedited censored values
|
// Logic to prevent overwriting unedited censored values
|
||||||
const finalCredentials = { ...credentials };
|
const finalCredentials = { ...credentials };
|
||||||
if (credentials.password === "******") {
|
if (credentials.password === "__INFISICAL_UNCHANGED__") {
|
||||||
const decryptedCredentials = await decryptAccountCredentials({
|
const decryptedCredentials = await decryptAccountCredentials({
|
||||||
encryptedCredentials: account.encryptedCredentials,
|
encryptedCredentials: account.encryptedCredentials,
|
||||||
projectId: account.projectId,
|
projectId: account.projectId,
|
||||||
@@ -239,7 +280,12 @@ export const pamAccountServiceFactory = ({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...(await decryptAccount(updatedAccount, account.projectId, kmsService)),
|
...(await decryptAccount(updatedAccount, account.projectId, kmsService)),
|
||||||
resource: { id: resource.id, name: resource.name, resourceType: resource.resourceType }
|
resource: {
|
||||||
|
id: resource.id,
|
||||||
|
name: resource.name,
|
||||||
|
resourceType: resource.resourceType,
|
||||||
|
rotationCredentialsConfigured: !!resource.encryptedRotationAccountCredentials
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -278,7 +324,12 @@ export const pamAccountServiceFactory = ({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...(await decryptAccount(deletedAccount, account.projectId, kmsService)),
|
...(await decryptAccount(deletedAccount, account.projectId, kmsService)),
|
||||||
resource: { id: resource.id, name: resource.name, resourceType: resource.resourceType }
|
resource: {
|
||||||
|
id: resource.id,
|
||||||
|
name: resource.name,
|
||||||
|
resourceType: resource.resourceType,
|
||||||
|
rotationCredentialsConfigured: !!resource.encryptedRotationAccountCredentials
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -300,7 +351,7 @@ export const pamAccountServiceFactory = ({
|
|||||||
|
|
||||||
const decryptedAndPermittedAccounts: Array<
|
const decryptedAndPermittedAccounts: Array<
|
||||||
TPamAccounts & {
|
TPamAccounts & {
|
||||||
resource: Pick<TPamResources, "id" | "name" | "resourceType">;
|
resource: Pick<TPamResources, "id" | "name" | "resourceType"> & { rotationCredentialsConfigured: boolean };
|
||||||
credentials: TPamAccountCredentials;
|
credentials: TPamAccountCredentials;
|
||||||
}
|
}
|
||||||
> = [];
|
> = [];
|
||||||
@@ -330,7 +381,8 @@ export const pamAccountServiceFactory = ({
|
|||||||
resource: {
|
resource: {
|
||||||
id: account.resource.id,
|
id: account.resource.id,
|
||||||
name: account.resource.name,
|
name: account.resource.name,
|
||||||
resourceType: account.resource.resourceType
|
resourceType: account.resource.resourceType,
|
||||||
|
rotationCredentialsConfigured: !!account.resource.encryptedRotationAccountCredentials
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -459,13 +511,14 @@ export const pamAccountServiceFactory = ({
|
|||||||
const project = await projectDAL.findById(session.projectId);
|
const project = await projectDAL.findById(session.projectId);
|
||||||
if (!project) throw new NotFoundError({ message: `Project with ID '${session.projectId}' not found` });
|
if (!project) throw new NotFoundError({ message: `Project with ID '${session.projectId}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
actor.type,
|
actor: actor.type,
|
||||||
actor.id,
|
actorId: actor.id,
|
||||||
project.orgId,
|
orgId: project.orgId,
|
||||||
actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actor.orgId
|
actorOrgId: actor.orgId,
|
||||||
);
|
scope: OrganizationActionScope.Any
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionGatewayActions.CreateGateways,
|
OrgPermissionGatewayActions.CreateGateways,
|
||||||
@@ -516,12 +569,116 @@ export const pamAccountServiceFactory = ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const rotateAllDueAccounts = async () => {
|
||||||
|
const accounts = await pamAccountDAL.findAccountsDueForRotation();
|
||||||
|
|
||||||
|
for (let i = 0; i < accounts.length; i += ROTATION_CONCURRENCY_LIMIT) {
|
||||||
|
const batch = accounts.slice(i, i + ROTATION_CONCURRENCY_LIMIT);
|
||||||
|
|
||||||
|
const rotationPromises = batch.map(async (account) =>
|
||||||
|
pamAccountDAL.transaction(async (tx) => {
|
||||||
|
let logResourceType = "unknown";
|
||||||
|
try {
|
||||||
|
const resource = await pamResourceDAL.findById(account.resourceId, tx);
|
||||||
|
if (!resource || !resource.encryptedRotationAccountCredentials) return;
|
||||||
|
logResourceType = resource.resourceType;
|
||||||
|
|
||||||
|
const { connectionDetails, rotationAccountCredentials, gatewayId, resourceType } = await decryptResource(
|
||||||
|
resource,
|
||||||
|
account.projectId,
|
||||||
|
kmsService
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!rotationAccountCredentials) return;
|
||||||
|
|
||||||
|
const accountCredentials = await decryptAccountCredentials({
|
||||||
|
encryptedCredentials: account.encryptedCredentials,
|
||||||
|
projectId: account.projectId,
|
||||||
|
kmsService
|
||||||
|
});
|
||||||
|
|
||||||
|
const factory = PAM_RESOURCE_FACTORY_MAP[resourceType as PamResource](
|
||||||
|
resourceType as PamResource,
|
||||||
|
connectionDetails,
|
||||||
|
gatewayId,
|
||||||
|
gatewayV2Service
|
||||||
|
);
|
||||||
|
|
||||||
|
const newCredentials = await factory.rotateAccountCredentials(
|
||||||
|
rotationAccountCredentials,
|
||||||
|
accountCredentials
|
||||||
|
);
|
||||||
|
|
||||||
|
const encryptedCredentials = await encryptAccountCredentials({
|
||||||
|
credentials: newCredentials,
|
||||||
|
projectId: account.projectId,
|
||||||
|
kmsService
|
||||||
|
});
|
||||||
|
|
||||||
|
await pamAccountDAL.updateById(
|
||||||
|
account.id,
|
||||||
|
{
|
||||||
|
encryptedCredentials,
|
||||||
|
lastRotatedAt: new Date()
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
|
await auditLogService.createAuditLog({
|
||||||
|
projectId: account.projectId,
|
||||||
|
actor: {
|
||||||
|
type: ActorType.PLATFORM,
|
||||||
|
metadata: {}
|
||||||
|
},
|
||||||
|
event: {
|
||||||
|
type: EventType.PAM_ACCOUNT_CREDENTIAL_ROTATION,
|
||||||
|
metadata: {
|
||||||
|
accountId: account.id,
|
||||||
|
accountName: account.name,
|
||||||
|
resourceId: resource.id,
|
||||||
|
resourceType: logResourceType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(error, `Failed to rotate credentials for account [accountId=${account.id}]`);
|
||||||
|
|
||||||
|
const errorMessage = error instanceof Error ? error.message : "An unknown error occurred";
|
||||||
|
|
||||||
|
await auditLogService.createAuditLog({
|
||||||
|
projectId: account.projectId,
|
||||||
|
actor: {
|
||||||
|
type: ActorType.PLATFORM,
|
||||||
|
metadata: {}
|
||||||
|
},
|
||||||
|
event: {
|
||||||
|
type: EventType.PAM_ACCOUNT_CREDENTIAL_ROTATION_FAILED,
|
||||||
|
metadata: {
|
||||||
|
accountId: account.id,
|
||||||
|
accountName: account.name,
|
||||||
|
resourceId: account.resourceId,
|
||||||
|
resourceType: logResourceType,
|
||||||
|
errorMessage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
throw error; // Rollback transaction
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await Promise.all(rotationPromises);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
create,
|
create,
|
||||||
updateById,
|
updateById,
|
||||||
deleteById,
|
deleteById,
|
||||||
list,
|
list,
|
||||||
access,
|
access,
|
||||||
getSessionCredentials
|
getSessionCredentials,
|
||||||
|
rotateAllDueAccounts
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import { TPamAccount } from "../pam-resource/pam-resource-types";
|
import { TPamAccount } from "../pam-resource/pam-resource-types";
|
||||||
|
|
||||||
// DTOs
|
// DTOs
|
||||||
export type TCreateAccountDTO = Pick<TPamAccount, "name" | "description" | "credentials" | "folderId" | "resourceId">;
|
export type TCreateAccountDTO = Pick<
|
||||||
|
TPamAccount,
|
||||||
|
"name" | "description" | "credentials" | "folderId" | "resourceId" | "rotationEnabled" | "rotationIntervalSeconds"
|
||||||
|
>;
|
||||||
|
|
||||||
export type TUpdateAccountDTO = Partial<Omit<TCreateAccountDTO, "folderId" | "resourceId">> & {
|
export type TUpdateAccountDTO = Partial<Omit<TCreateAccountDTO, "folderId" | "resourceId">> & {
|
||||||
accountId: string;
|
accountId: string;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { TPamResources } from "@app/db/schemas";
|
|||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
|
|
||||||
|
import { decryptAccountCredentials } from "../pam-account/pam-account-fns";
|
||||||
import { TPamResource, TPamResourceConnectionDetails } from "./pam-resource-types";
|
import { TPamResource, TPamResourceConnectionDetails } from "./pam-resource-types";
|
||||||
import { getPostgresResourceListItem } from "./postgres/postgres-resource-fns";
|
import { getPostgresResourceListItem } from "./postgres/postgres-resource-fns";
|
||||||
|
|
||||||
@@ -63,6 +64,13 @@ export const decryptResource = async (
|
|||||||
encryptedConnectionDetails: resource.encryptedConnectionDetails,
|
encryptedConnectionDetails: resource.encryptedConnectionDetails,
|
||||||
projectId,
|
projectId,
|
||||||
kmsService
|
kmsService
|
||||||
|
}),
|
||||||
|
rotationAccountCredentials: resource.encryptedRotationAccountCredentials
|
||||||
|
? await decryptAccountCredentials({
|
||||||
|
encryptedCredentials: resource.encryptedRotationAccountCredentials,
|
||||||
|
projectId,
|
||||||
|
kmsService
|
||||||
})
|
})
|
||||||
|
: null
|
||||||
} as TPamResource;
|
} as TPamResource;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { slugSchema } from "@app/server/lib/schemas";
|
|||||||
// Resources
|
// Resources
|
||||||
export const BasePamResourceSchema = PamResourcesSchema.omit({
|
export const BasePamResourceSchema = PamResourcesSchema.omit({
|
||||||
encryptedConnectionDetails: true,
|
encryptedConnectionDetails: true,
|
||||||
|
encryptedRotationAccountCredentials: true,
|
||||||
resourceType: true
|
resourceType: true
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -30,6 +31,8 @@ export const BasePamAccountSchemaWithResource = BasePamAccountSchema.extend({
|
|||||||
id: true,
|
id: true,
|
||||||
name: true,
|
name: true,
|
||||||
resourceType: true
|
resourceType: true
|
||||||
|
}).extend({
|
||||||
|
rotationCredentialsConfigured: z.boolean()
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -37,10 +40,14 @@ export const BaseCreatePamAccountSchema = z.object({
|
|||||||
resourceId: z.string().uuid(),
|
resourceId: z.string().uuid(),
|
||||||
folderId: z.string().uuid().optional(),
|
folderId: z.string().uuid().optional(),
|
||||||
name: slugSchema({ field: "name" }),
|
name: slugSchema({ field: "name" }),
|
||||||
description: z.string().max(512).nullable().optional()
|
description: z.string().max(512).nullable().optional(),
|
||||||
|
rotationEnabled: z.boolean(),
|
||||||
|
rotationIntervalSeconds: z.number().min(3600).nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const BaseUpdatePamAccountSchema = z.object({
|
export const BaseUpdatePamAccountSchema = z.object({
|
||||||
name: slugSchema({ field: "name" }).optional(),
|
name: slugSchema({ field: "name" }).optional(),
|
||||||
description: z.string().max(512).nullable().optional()
|
description: z.string().max(512).nullable().optional(),
|
||||||
|
rotationEnabled: z.boolean().optional(),
|
||||||
|
rotationIntervalSeconds: z.number().min(3600).nullable().optional()
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,10 +10,16 @@ import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
|||||||
|
|
||||||
import { TGatewayV2ServiceFactory } from "../gateway-v2/gateway-v2-service";
|
import { TGatewayV2ServiceFactory } from "../gateway-v2/gateway-v2-service";
|
||||||
import { TLicenseServiceFactory } from "../license/license-service";
|
import { TLicenseServiceFactory } from "../license/license-service";
|
||||||
|
import { decryptAccountCredentials, encryptAccountCredentials } from "../pam-account/pam-account-fns";
|
||||||
import { TPamResourceDALFactory } from "./pam-resource-dal";
|
import { TPamResourceDALFactory } from "./pam-resource-dal";
|
||||||
import { PamResource } from "./pam-resource-enums";
|
import { PamResource } from "./pam-resource-enums";
|
||||||
import { PAM_RESOURCE_FACTORY_MAP } from "./pam-resource-factory";
|
import { PAM_RESOURCE_FACTORY_MAP } from "./pam-resource-factory";
|
||||||
import { decryptResource, encryptResourceConnectionDetails, listResourceOptions } from "./pam-resource-fns";
|
import {
|
||||||
|
decryptResource,
|
||||||
|
decryptResourceConnectionDetails,
|
||||||
|
encryptResourceConnectionDetails,
|
||||||
|
listResourceOptions
|
||||||
|
} from "./pam-resource-fns";
|
||||||
import { TCreateResourceDTO, TUpdateResourceDTO } from "./pam-resource-types";
|
import { TCreateResourceDTO, TUpdateResourceDTO } from "./pam-resource-types";
|
||||||
|
|
||||||
type TPamResourceServiceFactoryDep = {
|
type TPamResourceServiceFactoryDep = {
|
||||||
@@ -61,7 +67,7 @@ export const pamResourceServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const create = async (
|
const create = async (
|
||||||
{ resourceType, connectionDetails, gatewayId, name, projectId }: TCreateResourceDTO,
|
{ resourceType, connectionDetails, gatewayId, name, projectId, rotationAccountCredentials }: TCreateResourceDTO,
|
||||||
actor: OrgServiceActor
|
actor: OrgServiceActor
|
||||||
) => {
|
) => {
|
||||||
const orgLicensePlan = await licenseService.getPlan(actor.orgId);
|
const orgLicensePlan = await licenseService.getPlan(actor.orgId);
|
||||||
@@ -88,26 +94,42 @@ export const pamResourceServiceFactory = ({
|
|||||||
gatewayId,
|
gatewayId,
|
||||||
gatewayV2Service
|
gatewayV2Service
|
||||||
);
|
);
|
||||||
const validatedConnectionDetails = await factory.validateConnection();
|
|
||||||
|
|
||||||
|
const validatedConnectionDetails = await factory.validateConnection();
|
||||||
const encryptedConnectionDetails = await encryptResourceConnectionDetails({
|
const encryptedConnectionDetails = await encryptResourceConnectionDetails({
|
||||||
connectionDetails: validatedConnectionDetails,
|
connectionDetails: validatedConnectionDetails,
|
||||||
projectId,
|
projectId,
|
||||||
kmsService
|
kmsService
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let encryptedRotationAccountCredentials: Buffer | null = null;
|
||||||
|
|
||||||
|
if (rotationAccountCredentials) {
|
||||||
|
const validatedRotationAccountCredentials = await factory.validateAccountCredentials(rotationAccountCredentials);
|
||||||
|
|
||||||
|
encryptedRotationAccountCredentials = await encryptAccountCredentials({
|
||||||
|
credentials: validatedRotationAccountCredentials,
|
||||||
|
projectId,
|
||||||
|
kmsService
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const resource = await pamResourceDAL.create({
|
const resource = await pamResourceDAL.create({
|
||||||
resourceType,
|
resourceType,
|
||||||
encryptedConnectionDetails,
|
encryptedConnectionDetails,
|
||||||
gatewayId,
|
gatewayId,
|
||||||
name,
|
name,
|
||||||
projectId
|
projectId,
|
||||||
|
encryptedRotationAccountCredentials
|
||||||
});
|
});
|
||||||
|
|
||||||
return decryptResource(resource, projectId, kmsService);
|
return decryptResource(resource, projectId, kmsService);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateById = async ({ connectionDetails, resourceId, name }: TUpdateResourceDTO, actor: OrgServiceActor) => {
|
const updateById = async (
|
||||||
|
{ connectionDetails, resourceId, name, rotationAccountCredentials }: TUpdateResourceDTO,
|
||||||
|
actor: OrgServiceActor
|
||||||
|
) => {
|
||||||
const orgLicensePlan = await licenseService.getPlan(actor.orgId);
|
const orgLicensePlan = await licenseService.getPlan(actor.orgId);
|
||||||
if (!orgLicensePlan.pam) {
|
if (!orgLicensePlan.pam) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
@@ -151,6 +173,60 @@ export const pamResourceServiceFactory = ({
|
|||||||
updateDoc.encryptedConnectionDetails = encryptedConnectionDetails;
|
updateDoc.encryptedConnectionDetails = encryptedConnectionDetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rotationAccountCredentials !== undefined) {
|
||||||
|
updateDoc.encryptedRotationAccountCredentials = null;
|
||||||
|
|
||||||
|
if (rotationAccountCredentials) {
|
||||||
|
const decryptedConnectionDetails =
|
||||||
|
connectionDetails ??
|
||||||
|
(await decryptResourceConnectionDetails({
|
||||||
|
encryptedConnectionDetails: resource.encryptedConnectionDetails,
|
||||||
|
projectId: resource.projectId,
|
||||||
|
kmsService
|
||||||
|
}));
|
||||||
|
|
||||||
|
const factory = PAM_RESOURCE_FACTORY_MAP[resource.resourceType as PamResource](
|
||||||
|
resource.resourceType as PamResource,
|
||||||
|
decryptedConnectionDetails,
|
||||||
|
resource.gatewayId,
|
||||||
|
gatewayV2Service
|
||||||
|
);
|
||||||
|
|
||||||
|
// Logic to prevent overwriting unedited censored values
|
||||||
|
const finalCredentials = { ...rotationAccountCredentials };
|
||||||
|
if (
|
||||||
|
resource.encryptedRotationAccountCredentials &&
|
||||||
|
rotationAccountCredentials.password === "__INFISICAL_UNCHANGED__"
|
||||||
|
) {
|
||||||
|
const decryptedCredentials = await decryptAccountCredentials({
|
||||||
|
encryptedCredentials: resource.encryptedRotationAccountCredentials,
|
||||||
|
projectId: resource.projectId,
|
||||||
|
kmsService
|
||||||
|
});
|
||||||
|
|
||||||
|
finalCredentials.password = decryptedCredentials.password;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const validatedRotationAccountCredentials = await factory.validateAccountCredentials(finalCredentials);
|
||||||
|
|
||||||
|
updateDoc.encryptedRotationAccountCredentials = await encryptAccountCredentials({
|
||||||
|
credentials: validatedRotationAccountCredentials,
|
||||||
|
projectId: resource.projectId,
|
||||||
|
kmsService
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof BadRequestError) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `Rotation Account Error: ${err.message}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If nothing was updated, return the fetched resource
|
// If nothing was updated, return the fetched resource
|
||||||
if (Object.keys(updateDoc).length === 0) {
|
if (Object.keys(updateDoc).length === 0) {
|
||||||
return decryptResource(resource, resource.projectId, kmsService);
|
return decryptResource(resource, resource.projectId, kmsService);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export type TPamAccountCredentials = TPostgresAccountCredentials;
|
|||||||
// Resource DTOs
|
// Resource DTOs
|
||||||
export type TCreateResourceDTO = Pick<
|
export type TCreateResourceDTO = Pick<
|
||||||
TPamResource,
|
TPamResource,
|
||||||
"name" | "connectionDetails" | "resourceType" | "gatewayId" | "projectId"
|
"name" | "connectionDetails" | "resourceType" | "gatewayId" | "projectId" | "rotationAccountCredentials"
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export type TUpdateResourceDTO = Partial<Omit<TCreateResourceDTO, "resourceType" | "projectId">> & {
|
export type TUpdateResourceDTO = Partial<Omit<TCreateResourceDTO, "resourceType" | "projectId">> & {
|
||||||
@@ -30,6 +30,10 @@ export type TPamResourceFactoryValidateConnection<T extends TPamResourceConnecti
|
|||||||
export type TPamResourceFactoryValidateAccountCredentials<C extends TPamAccountCredentials> = (
|
export type TPamResourceFactoryValidateAccountCredentials<C extends TPamAccountCredentials> = (
|
||||||
credentials: C
|
credentials: C
|
||||||
) => Promise<C>;
|
) => Promise<C>;
|
||||||
|
export type TPamResourceFactoryRotateAccountCredentials<C extends TPamAccountCredentials> = (
|
||||||
|
rotationAccountCredentials: C,
|
||||||
|
currentCredentials: C
|
||||||
|
) => Promise<C>;
|
||||||
|
|
||||||
export type TPamResourceFactory<T extends TPamResourceConnectionDetails, C extends TPamAccountCredentials> = (
|
export type TPamResourceFactory<T extends TPamResourceConnectionDetails, C extends TPamAccountCredentials> = (
|
||||||
resourceType: PamResource,
|
resourceType: PamResource,
|
||||||
@@ -39,4 +43,5 @@ export type TPamResourceFactory<T extends TPamResourceConnectionDetails, C exten
|
|||||||
) => {
|
) => {
|
||||||
validateConnection: TPamResourceFactoryValidateConnection<T>;
|
validateConnection: TPamResourceFactoryValidateConnection<T>;
|
||||||
validateAccountCredentials: TPamResourceFactoryValidateAccountCredentials<C>;
|
validateAccountCredentials: TPamResourceFactoryValidateAccountCredentials<C>;
|
||||||
|
rotateAccountCredentials: TPamResourceFactoryRotateAccountCredentials<C>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,13 +15,24 @@ import {
|
|||||||
BaseSqlResourceConnectionDetailsSchema
|
BaseSqlResourceConnectionDetailsSchema
|
||||||
} from "../shared/sql/sql-resource-schemas";
|
} from "../shared/sql/sql-resource-schemas";
|
||||||
|
|
||||||
// Resources
|
|
||||||
export const PostgresResourceConnectionDetailsSchema = BaseSqlResourceConnectionDetailsSchema;
|
export const PostgresResourceConnectionDetailsSchema = BaseSqlResourceConnectionDetailsSchema;
|
||||||
|
export const PostgresAccountCredentialsSchema = BaseSqlAccountCredentialsSchema;
|
||||||
|
|
||||||
|
// Resources
|
||||||
const BasePostgresResourceSchema = BasePamResourceSchema.extend({ resourceType: z.literal(PamResource.Postgres) });
|
const BasePostgresResourceSchema = BasePamResourceSchema.extend({ resourceType: z.literal(PamResource.Postgres) });
|
||||||
|
|
||||||
export const PostgresResourceSchema = BasePostgresResourceSchema.extend({
|
export const PostgresResourceSchema = BasePostgresResourceSchema.extend({
|
||||||
connectionDetails: PostgresResourceConnectionDetailsSchema
|
connectionDetails: PostgresResourceConnectionDetailsSchema,
|
||||||
|
rotationAccountCredentials: PostgresAccountCredentialsSchema.nullable().optional()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SanitizedPostgresResourceSchema = BasePostgresResourceSchema.extend({
|
||||||
|
connectionDetails: PostgresResourceConnectionDetailsSchema,
|
||||||
|
rotationAccountCredentials: PostgresAccountCredentialsSchema.pick({
|
||||||
|
username: true
|
||||||
|
})
|
||||||
|
.nullable()
|
||||||
|
.optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const PostgresResourceListItemSchema = z.object({
|
export const PostgresResourceListItemSchema = z.object({
|
||||||
@@ -30,16 +41,16 @@ export const PostgresResourceListItemSchema = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const CreatePostgresResourceSchema = BaseCreatePamResourceSchema.extend({
|
export const CreatePostgresResourceSchema = BaseCreatePamResourceSchema.extend({
|
||||||
connectionDetails: PostgresResourceConnectionDetailsSchema
|
connectionDetails: PostgresResourceConnectionDetailsSchema,
|
||||||
|
rotationAccountCredentials: PostgresAccountCredentialsSchema.nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const UpdatePostgresResourceSchema = BaseUpdatePamResourceSchema.extend({
|
export const UpdatePostgresResourceSchema = BaseUpdatePamResourceSchema.extend({
|
||||||
connectionDetails: PostgresResourceConnectionDetailsSchema.optional()
|
connectionDetails: PostgresResourceConnectionDetailsSchema.optional(),
|
||||||
|
rotationAccountCredentials: PostgresAccountCredentialsSchema.nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
// Accounts
|
// Accounts
|
||||||
export const PostgresAccountCredentialsSchema = BaseSqlAccountCredentialsSchema;
|
|
||||||
|
|
||||||
export const PostgresAccountSchema = BasePamAccountSchema.extend({
|
export const PostgresAccountSchema = BasePamAccountSchema.extend({
|
||||||
credentials: PostgresAccountCredentialsSchema
|
credentials: PostgresAccountCredentialsSchema
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,9 +6,14 @@ import { TGatewayV2ServiceFactory } from "@app/ee/services/gateway-v2/gateway-v2
|
|||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { GatewayProxyProtocol } from "@app/lib/gateway";
|
import { GatewayProxyProtocol } from "@app/lib/gateway";
|
||||||
import { withGatewayV2Proxy } from "@app/lib/gateway-v2/gateway-v2";
|
import { withGatewayV2Proxy } from "@app/lib/gateway-v2/gateway-v2";
|
||||||
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
|
|
||||||
import { PamResource } from "../../pam-resource-enums";
|
import { PamResource } from "../../pam-resource-enums";
|
||||||
import { TPamResourceFactory, TPamResourceFactoryValidateAccountCredentials } from "../../pam-resource-types";
|
import {
|
||||||
|
TPamResourceFactory,
|
||||||
|
TPamResourceFactoryRotateAccountCredentials,
|
||||||
|
TPamResourceFactoryValidateAccountCredentials
|
||||||
|
} from "../../pam-resource-types";
|
||||||
import { TSqlAccountCredentials, TSqlResourceConnectionDetails } from "./sql-resource-types";
|
import { TSqlAccountCredentials, TSqlResourceConnectionDetails } from "./sql-resource-types";
|
||||||
|
|
||||||
const EXTERNAL_REQUEST_TIMEOUT = 10 * 1000;
|
const EXTERNAL_REQUEST_TIMEOUT = 10 * 1000;
|
||||||
@@ -176,8 +181,66 @@ export const sqlResourceFactory: TPamResourceFactory<TSqlResourceConnectionDetai
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const rotateAccountCredentials: TPamResourceFactoryRotateAccountCredentials<TSqlAccountCredentials> = async (
|
||||||
|
rotationAccountCredentials,
|
||||||
|
currentCredentials
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const newPassword = alphaNumericNanoId(32);
|
||||||
|
|
||||||
|
await executeWithGateway(
|
||||||
|
{
|
||||||
|
connectionDetails,
|
||||||
|
gatewayId,
|
||||||
|
resourceType,
|
||||||
|
username: rotationAccountCredentials.username,
|
||||||
|
password: rotationAccountCredentials.password
|
||||||
|
},
|
||||||
|
gatewayV2Service,
|
||||||
|
async (client) => {
|
||||||
|
switch (resourceType) {
|
||||||
|
case PamResource.Postgres:
|
||||||
|
await client.raw(`ALTER USER ?? WITH PASSWORD '${newPassword}'`, [currentCredentials.username]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `Password rotation for ${resourceType as PamResource} is not supported.`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return { username: currentCredentials.username, password: newPassword };
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof BadRequestError) {
|
||||||
|
if (error.message === `password authentication failed for user "${rotationAccountCredentials.username}"`) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Management credentials invalid: Username or password incorrect"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.message.includes("permission denied")) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `Management credentials lack permission to rotate password for user "${currentCredentials.username}"`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.message === "Connection terminated unexpectedly") {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Connection terminated unexpectedly. Verify that host and port are correct"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `Unable to rotate account credentials for ${resourceType}: ${(error as Error).message || String(error)}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
validateConnection,
|
validateConnection,
|
||||||
validateAccountCredentials
|
validateAccountCredentials,
|
||||||
|
rotateAccountCredentials
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,6 +16,6 @@ export const BaseSqlResourceConnectionDetailsSchema = z.object({
|
|||||||
|
|
||||||
// Accounts
|
// Accounts
|
||||||
export const BaseSqlAccountCredentialsSchema = z.object({
|
export const BaseSqlAccountCredentialsSchema = z.object({
|
||||||
username: z.string().trim().min(1),
|
username: z.string().trim().min(1).max(63),
|
||||||
password: z.string().trim().min(1)
|
password: z.string().trim().min(1).max(256)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
import { ActionProjectType } from "@app/db/schemas";
|
import { ActionProjectType, OrganizationActionScope } from "@app/db/schemas";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { OrgServiceActor } from "@app/lib/types";
|
import { OrgServiceActor } from "@app/lib/types";
|
||||||
@@ -102,13 +102,14 @@ export const pamSessionServiceFactory = ({
|
|||||||
const project = await projectDAL.findById(session.projectId);
|
const project = await projectDAL.findById(session.projectId);
|
||||||
if (!project) throw new NotFoundError({ message: `Project with ID '${session.projectId}' not found` });
|
if (!project) throw new NotFoundError({ message: `Project with ID '${session.projectId}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
actor.type,
|
actor: actor.type,
|
||||||
actor.id,
|
actorId: actor.id,
|
||||||
project.orgId,
|
orgId: project.orgId,
|
||||||
actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actor.orgId
|
actorOrgId: actor.orgId,
|
||||||
);
|
scope: OrganizationActionScope.Any
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionGatewayActions.CreateGateways,
|
OrgPermissionGatewayActions.CreateGateways,
|
||||||
@@ -142,13 +143,14 @@ export const pamSessionServiceFactory = ({
|
|||||||
const project = await projectDAL.findById(session.projectId);
|
const project = await projectDAL.findById(session.projectId);
|
||||||
if (!project) throw new NotFoundError({ message: `Project with ID '${session.projectId}' not found` });
|
if (!project) throw new NotFoundError({ message: `Project with ID '${session.projectId}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
actor.type,
|
actor: actor.type,
|
||||||
actor.id,
|
actorId: actor.id,
|
||||||
project.orgId,
|
orgId: project.orgId,
|
||||||
actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actor.orgId
|
actorOrgId: actor.orgId,
|
||||||
);
|
scope: OrganizationActionScope.Any
|
||||||
|
});
|
||||||
|
|
||||||
if (actor.type === ActorType.IDENTITY) {
|
if (actor.type === ActorType.IDENTITY) {
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ export enum OrgPermissionActions {
|
|||||||
Delete = "delete"
|
Delete = "delete"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum OrgPermissionSubOrgActions {
|
||||||
|
Create = "create",
|
||||||
|
DirectAccess = "direct-access"
|
||||||
|
}
|
||||||
|
|
||||||
export enum OrgPermissionAppConnectionActions {
|
export enum OrgPermissionAppConnectionActions {
|
||||||
Read = "read",
|
Read = "read",
|
||||||
Create = "create",
|
Create = "create",
|
||||||
@@ -117,7 +122,8 @@ export enum OrgPermissionSubjects {
|
|||||||
Kmip = "kmip",
|
Kmip = "kmip",
|
||||||
Gateway = "gateway",
|
Gateway = "gateway",
|
||||||
Relay = "relay",
|
Relay = "relay",
|
||||||
SecretShare = "secret-share"
|
SecretShare = "secret-share",
|
||||||
|
SubOrganization = "sub-organization"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AppConnectionSubjectFields = {
|
export type AppConnectionSubjectFields = {
|
||||||
@@ -128,6 +134,7 @@ export type OrgPermissionSet =
|
|||||||
| [OrgPermissionActions.Create, OrgPermissionSubjects.Workspace]
|
| [OrgPermissionActions.Create, OrgPermissionSubjects.Workspace]
|
||||||
| [OrgPermissionActions.Create, OrgPermissionSubjects.Project]
|
| [OrgPermissionActions.Create, OrgPermissionSubjects.Project]
|
||||||
| [OrgPermissionActions, OrgPermissionSubjects.Role]
|
| [OrgPermissionActions, OrgPermissionSubjects.Role]
|
||||||
|
| [OrgPermissionSubOrgActions, OrgPermissionSubjects.SubOrganization]
|
||||||
| [OrgPermissionActions, OrgPermissionSubjects.Member]
|
| [OrgPermissionActions, OrgPermissionSubjects.Member]
|
||||||
| [OrgPermissionActions, OrgPermissionSubjects.Settings]
|
| [OrgPermissionActions, OrgPermissionSubjects.Settings]
|
||||||
| [OrgPermissionActions, OrgPermissionSubjects.IncidentAccount]
|
| [OrgPermissionActions, OrgPermissionSubjects.IncidentAccount]
|
||||||
@@ -185,6 +192,12 @@ export const OrgPermissionSchema = z.discriminatedUnion("subject", [
|
|||||||
subject: z.literal(OrgPermissionSubjects.Role).describe("The entity this permission pertains to."),
|
subject: z.literal(OrgPermissionSubjects.Role).describe("The entity this permission pertains to."),
|
||||||
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
|
||||||
}),
|
}),
|
||||||
|
z.object({
|
||||||
|
subject: z.literal(OrgPermissionSubjects.SubOrganization).describe("The entity this permission pertains to."),
|
||||||
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionSubOrgActions).describe(
|
||||||
|
"Describe what action an entity can take."
|
||||||
|
)
|
||||||
|
}),
|
||||||
z.object({
|
z.object({
|
||||||
subject: z.literal(OrgPermissionSubjects.Member).describe("The entity this permission pertains to."),
|
subject: z.literal(OrgPermissionSubjects.Member).describe("The entity this permission pertains to."),
|
||||||
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
|
||||||
@@ -308,6 +321,10 @@ const buildAdminPermission = () => {
|
|||||||
// ws permissions
|
// ws permissions
|
||||||
can(OrgPermissionActions.Create, OrgPermissionSubjects.Workspace);
|
can(OrgPermissionActions.Create, OrgPermissionSubjects.Workspace);
|
||||||
can(OrgPermissionActions.Create, OrgPermissionSubjects.Project);
|
can(OrgPermissionActions.Create, OrgPermissionSubjects.Project);
|
||||||
|
|
||||||
|
can(OrgPermissionSubOrgActions.Create, OrgPermissionSubjects.SubOrganization);
|
||||||
|
can(OrgPermissionSubOrgActions.DirectAccess, OrgPermissionSubjects.SubOrganization);
|
||||||
|
|
||||||
// role permission
|
// role permission
|
||||||
can(OrgPermissionActions.Read, OrgPermissionSubjects.Role);
|
can(OrgPermissionActions.Read, OrgPermissionSubjects.Role);
|
||||||
can(OrgPermissionActions.Create, OrgPermissionSubjects.Role);
|
can(OrgPermissionActions.Create, OrgPermissionSubjects.Role);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ interface TPermissionDataReturn extends TMemberships {
|
|||||||
orgAuthEnforced?: boolean | null;
|
orgAuthEnforced?: boolean | null;
|
||||||
orgGoogleSsoAuthEnforced?: boolean | null;
|
orgGoogleSsoAuthEnforced?: boolean | null;
|
||||||
shouldUseNewPrivilegeSystem?: boolean | null;
|
shouldUseNewPrivilegeSystem?: boolean | null;
|
||||||
|
rootOrgId?: string | null;
|
||||||
bypassOrgAuthEnabled?: boolean | null;
|
bypassOrgAuthEnabled?: boolean | null;
|
||||||
roles: {
|
roles: {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -273,7 +274,8 @@ export const permissionDALFactory = (db: TDbClient): TPermissionDALFactory => {
|
|||||||
db.ref("shouldUseNewPrivilegeSystem").withSchema(TableName.Organization),
|
db.ref("shouldUseNewPrivilegeSystem").withSchema(TableName.Organization),
|
||||||
db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"),
|
db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"),
|
||||||
db.ref("googleSsoAuthEnforced").withSchema(TableName.Organization).as("orgGoogleSsoAuthEnforced"),
|
db.ref("googleSsoAuthEnforced").withSchema(TableName.Organization).as("orgGoogleSsoAuthEnforced"),
|
||||||
db.ref("bypassOrgAuthEnabled").withSchema(TableName.Organization).as("bypassOrgAuthEnabled")
|
db.ref("bypassOrgAuthEnabled").withSchema(TableName.Organization).as("bypassOrgAuthEnabled"),
|
||||||
|
db.ref("rootOrgId").withSchema(TableName.Organization).as("rootOrgId")
|
||||||
);
|
);
|
||||||
|
|
||||||
const data = sqlNestRelationships({
|
const data = sqlNestRelationships({
|
||||||
@@ -283,6 +285,7 @@ export const permissionDALFactory = (db: TDbClient): TPermissionDALFactory => {
|
|||||||
MembershipsSchema.extend({
|
MembershipsSchema.extend({
|
||||||
orgAuthEnforced: z.boolean().optional().nullable(),
|
orgAuthEnforced: z.boolean().optional().nullable(),
|
||||||
shouldUseNewPrivilegeSystem: z.boolean().optional().nullable(),
|
shouldUseNewPrivilegeSystem: z.boolean().optional().nullable(),
|
||||||
|
rootOrgId: z.string().optional().nullable(),
|
||||||
orgGoogleSsoAuthEnforced: z.boolean(),
|
orgGoogleSsoAuthEnforced: z.boolean(),
|
||||||
bypassOrgAuthEnabled: z.boolean()
|
bypassOrgAuthEnabled: z.boolean()
|
||||||
}).parse(el),
|
}).parse(el),
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { MongoAbility } from "@casl/ability";
|
|||||||
import { MongoQuery } from "@ucast/mongo2js";
|
import { MongoQuery } from "@ucast/mongo2js";
|
||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { ActionProjectType, TMemberships } from "@app/db/schemas";
|
import { ActionProjectType, OrganizationActionScope, TMemberships } from "@app/db/schemas";
|
||||||
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
|
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
import { OrgPermissionSet } from "./org-permission";
|
import { OrgPermissionSet } from "./org-permission";
|
||||||
@@ -18,21 +18,6 @@ export type TBuildOrgPermissionDTO = {
|
|||||||
role: string;
|
role: string;
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
export type TGetUserProjectPermissionArg = {
|
|
||||||
userId: string;
|
|
||||||
projectId: string;
|
|
||||||
authMethod: ActorAuthMethod;
|
|
||||||
actionProjectType: ActionProjectType;
|
|
||||||
userOrgId?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TGetIdentityProjectPermissionArg = {
|
|
||||||
identityId: string;
|
|
||||||
projectId: string;
|
|
||||||
identityOrgId?: string;
|
|
||||||
actionProjectType: ActionProjectType;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TGetServiceTokenProjectPermissionArg = {
|
export type TGetServiceTokenProjectPermissionArg = {
|
||||||
serviceTokenId: string;
|
serviceTokenId: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
@@ -54,17 +39,12 @@ export type TGetOrgPermissionArg = {
|
|||||||
actorId: string;
|
actorId: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
actorAuthMethod: ActorAuthMethod;
|
actorAuthMethod: ActorAuthMethod;
|
||||||
actorOrgId?: string;
|
actorOrgId: string;
|
||||||
|
scope: OrganizationActionScope;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TPermissionServiceFactory = {
|
export type TPermissionServiceFactory = {
|
||||||
getOrgPermission: (
|
getOrgPermission: (arg: TGetOrgPermissionArg) => Promise<{
|
||||||
type: ActorType,
|
|
||||||
id: string,
|
|
||||||
orgId: string,
|
|
||||||
authMethod: ActorAuthMethod,
|
|
||||||
actorOrgId: string | undefined
|
|
||||||
) => Promise<{
|
|
||||||
permission: MongoAbility<OrgPermissionSet, MongoQuery>;
|
permission: MongoAbility<OrgPermissionSet, MongoQuery>;
|
||||||
memberships: Array<
|
memberships: Array<
|
||||||
TMemberships & {
|
TMemberships & {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { Knex } from "knex";
|
|||||||
import {
|
import {
|
||||||
AccessScope,
|
AccessScope,
|
||||||
ActionProjectType,
|
ActionProjectType,
|
||||||
|
OrganizationActionScope,
|
||||||
OrgMembershipRole,
|
OrgMembershipRole,
|
||||||
ProjectMembershipRole,
|
ProjectMembershipRole,
|
||||||
ServiceTokenScopes
|
ServiceTokenScopes
|
||||||
@@ -179,14 +180,15 @@ export const permissionServiceFactory = ({
|
|||||||
// return minTtl;
|
// return minTtl;
|
||||||
// };
|
// };
|
||||||
|
|
||||||
const getOrgPermission: TPermissionServiceFactory["getOrgPermission"] = async (
|
const getOrgPermission: TPermissionServiceFactory["getOrgPermission"] = async ({
|
||||||
type,
|
actor,
|
||||||
id,
|
actorId,
|
||||||
orgId,
|
orgId,
|
||||||
authMethod,
|
actorOrgId,
|
||||||
actorOrgId
|
scope,
|
||||||
) => {
|
actorAuthMethod
|
||||||
if (type !== ActorType.USER && type !== ActorType.IDENTITY) {
|
}) => {
|
||||||
|
if (actor !== ActorType.USER && actor !== ActorType.IDENTITY) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: "Invalid actor provided",
|
message: "Invalid actor provided",
|
||||||
name: "Get org permission"
|
name: "Get org permission"
|
||||||
@@ -202,11 +204,19 @@ export const permissionServiceFactory = ({
|
|||||||
scope: AccessScope.Organization,
|
scope: AccessScope.Organization,
|
||||||
orgId
|
orgId
|
||||||
},
|
},
|
||||||
actorId: id,
|
actorId,
|
||||||
actorType: type
|
actorType: actor
|
||||||
});
|
});
|
||||||
if (!permissionData?.length) throw new ForbiddenRequestError({ name: "You are not member of this organization" });
|
if (!permissionData?.length) throw new ForbiddenRequestError({ name: "You are not member of this organization" });
|
||||||
|
|
||||||
|
const rootOrgId = permissionData?.[0]?.rootOrgId;
|
||||||
|
const isChild = Boolean(rootOrgId);
|
||||||
|
if (scope === OrganizationActionScope.ParentOrganization && isChild) {
|
||||||
|
throw new ForbiddenRequestError({ message: `Child organization cannot do this operation` });
|
||||||
|
} else if (scope === OrganizationActionScope.ChildOrganization && !isChild) {
|
||||||
|
throw new ForbiddenRequestError({ message: `Parent organization cannot do this operation` });
|
||||||
|
}
|
||||||
|
|
||||||
const permissionFromRoles = permissionData.flatMap((membership) => {
|
const permissionFromRoles = permissionData.flatMap((membership) => {
|
||||||
const activeRoles = membership?.roles
|
const activeRoles = membership?.roles
|
||||||
.filter(
|
.filter(
|
||||||
@@ -227,7 +237,7 @@ export const permissionServiceFactory = ({
|
|||||||
permissionData.some((memberships) => memberships.roles.some((el) => role === (el.customRoleSlug || el.role)));
|
permissionData.some((memberships) => memberships.roles.some((el) => role === (el.customRoleSlug || el.role)));
|
||||||
|
|
||||||
validateOrgSSO(
|
validateOrgSSO(
|
||||||
authMethod,
|
actorAuthMethod,
|
||||||
permissionData?.[0].orgAuthEnforced,
|
permissionData?.[0].orgAuthEnforced,
|
||||||
Boolean(permissionData?.[0].orgGoogleSsoAuthEnforced),
|
Boolean(permissionData?.[0].orgGoogleSsoAuthEnforced),
|
||||||
Boolean(permissionData?.[0].bypassOrgAuthEnabled),
|
Boolean(permissionData?.[0].bypassOrgAuthEnabled),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import { packRules } from "@casl/ability/extra";
|
import { packRules } from "@casl/ability/extra";
|
||||||
|
|
||||||
import { ProjectType, TProjectTemplates } from "@app/db/schemas";
|
import { OrganizationActionScope, ProjectType, TProjectTemplates } 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-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
@@ -59,13 +59,14 @@ export const projectTemplateServiceFactory = ({
|
|||||||
message: "Failed to access project templates due to plan restriction. Upgrade plan to access project templates."
|
message: "Failed to access project templates due to plan restriction. Upgrade plan to access project templates."
|
||||||
});
|
});
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
actor.type,
|
actor: actor.type,
|
||||||
actor.id,
|
actorId: actor.id,
|
||||||
actor.orgId,
|
orgId: actor.orgId,
|
||||||
actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actor.orgId
|
actorOrgId: actor.orgId,
|
||||||
);
|
scope: OrganizationActionScope.Any
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.ProjectTemplates);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.ProjectTemplates);
|
||||||
|
|
||||||
@@ -97,13 +98,14 @@ export const projectTemplateServiceFactory = ({
|
|||||||
|
|
||||||
if (!projectTemplate) throw new NotFoundError({ message: `Could not find project template with Name "${name}"` });
|
if (!projectTemplate) throw new NotFoundError({ message: `Could not find project template with Name "${name}"` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
actor.type,
|
actor: actor.type,
|
||||||
actor.id,
|
actorId: actor.id,
|
||||||
projectTemplate.orgId,
|
orgId: projectTemplate.orgId,
|
||||||
actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actor.orgId
|
actorOrgId: actor.orgId,
|
||||||
);
|
scope: OrganizationActionScope.Any
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.ProjectTemplates);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.ProjectTemplates);
|
||||||
|
|
||||||
@@ -125,13 +127,14 @@ export const projectTemplateServiceFactory = ({
|
|||||||
|
|
||||||
if (!projectTemplate) throw new NotFoundError({ message: `Could not find project template with ID ${id}` });
|
if (!projectTemplate) throw new NotFoundError({ message: `Could not find project template with ID ${id}` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
actor.type,
|
actor: actor.type,
|
||||||
actor.id,
|
actorId: actor.id,
|
||||||
projectTemplate.orgId,
|
orgId: projectTemplate.orgId,
|
||||||
actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actor.orgId
|
actorOrgId: actor.orgId,
|
||||||
);
|
scope: OrganizationActionScope.Any
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.ProjectTemplates);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.ProjectTemplates);
|
||||||
|
|
||||||
@@ -152,13 +155,14 @@ export const projectTemplateServiceFactory = ({
|
|||||||
message: "Failed to create project template due to plan restriction. Upgrade plan to access project templates."
|
message: "Failed to create project template due to plan restriction. Upgrade plan to access project templates."
|
||||||
});
|
});
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
actor.type,
|
actor: actor.type,
|
||||||
actor.id,
|
actorId: actor.id,
|
||||||
actor.orgId,
|
orgId: actor.orgId,
|
||||||
actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actor.orgId
|
actorOrgId: actor.orgId,
|
||||||
);
|
scope: OrganizationActionScope.Any
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.ProjectTemplates);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.ProjectTemplates);
|
||||||
|
|
||||||
@@ -213,13 +217,14 @@ export const projectTemplateServiceFactory = ({
|
|||||||
|
|
||||||
if (!projectTemplate) throw new NotFoundError({ message: `Could not find project template with ID ${id}` });
|
if (!projectTemplate) throw new NotFoundError({ message: `Could not find project template with ID ${id}` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
actor.type,
|
actor: actor.type,
|
||||||
actor.id,
|
actorId: actor.id,
|
||||||
projectTemplate.orgId,
|
orgId: projectTemplate.orgId,
|
||||||
actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actor.orgId
|
actorOrgId: actor.orgId,
|
||||||
);
|
scope: OrganizationActionScope.Any
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.ProjectTemplates);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.ProjectTemplates);
|
||||||
if (projectTemplate.type !== ProjectType.SecretManager && environments)
|
if (projectTemplate.type !== ProjectType.SecretManager && environments)
|
||||||
@@ -272,13 +277,14 @@ export const projectTemplateServiceFactory = ({
|
|||||||
|
|
||||||
if (!projectTemplate) throw new NotFoundError({ message: `Could not find project template with ID ${id}` });
|
if (!projectTemplate) throw new NotFoundError({ message: `Could not find project template with ID ${id}` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
actor.type,
|
actor: actor.type,
|
||||||
actor.id,
|
actorId: actor.id,
|
||||||
projectTemplate.orgId,
|
orgId: projectTemplate.orgId,
|
||||||
actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actor.orgId
|
actorOrgId: actor.orgId,
|
||||||
);
|
scope: OrganizationActionScope.Any
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.ProjectTemplates);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.ProjectTemplates);
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { ProjectMembershipRole, ProjectType, TProjectEnvironments } from "@app/db/schemas";
|
import { ProjectMembershipRole, ProjectType, TProjectEnvironments } from "@app/db/schemas";
|
||||||
import { TProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
import { TProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
||||||
import { OrgServiceActor } from "@app/lib/types";
|
import { ProjectServiceActor } from "@app/lib/types";
|
||||||
import { UnpackedPermissionSchema } from "@app/server/routes/sanitizedSchema/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">;
|
||||||
@@ -31,7 +31,7 @@ export enum InfisicalProjectTemplate {
|
|||||||
|
|
||||||
export type TProjectTemplateServiceFactory = {
|
export type TProjectTemplateServiceFactory = {
|
||||||
listProjectTemplatesByOrg: (
|
listProjectTemplatesByOrg: (
|
||||||
actor: OrgServiceActor,
|
actor: ProjectServiceActor,
|
||||||
type?: ProjectType
|
type?: ProjectType
|
||||||
) => Promise<
|
) => Promise<
|
||||||
(
|
(
|
||||||
@@ -85,7 +85,7 @@ export type TProjectTemplateServiceFactory = {
|
|||||||
>;
|
>;
|
||||||
createProjectTemplate: (
|
createProjectTemplate: (
|
||||||
arg: TCreateProjectTemplateDTO,
|
arg: TCreateProjectTemplateDTO,
|
||||||
actor: OrgServiceActor
|
actor: ProjectServiceActor
|
||||||
) => Promise<{
|
) => Promise<{
|
||||||
environments: TProjectTemplateEnvironment[];
|
environments: TProjectTemplateEnvironment[];
|
||||||
roles: {
|
roles: {
|
||||||
@@ -109,7 +109,7 @@ export type TProjectTemplateServiceFactory = {
|
|||||||
updateProjectTemplateById: (
|
updateProjectTemplateById: (
|
||||||
id: string,
|
id: string,
|
||||||
{ roles, environments, ...params }: TUpdateProjectTemplateDTO,
|
{ roles, environments, ...params }: TUpdateProjectTemplateDTO,
|
||||||
actor: OrgServiceActor
|
actor: ProjectServiceActor
|
||||||
) => Promise<{
|
) => Promise<{
|
||||||
environments: TProjectTemplateEnvironment[];
|
environments: TProjectTemplateEnvironment[];
|
||||||
roles: {
|
roles: {
|
||||||
@@ -132,7 +132,7 @@ export type TProjectTemplateServiceFactory = {
|
|||||||
}>;
|
}>;
|
||||||
deleteProjectTemplateById: (
|
deleteProjectTemplateById: (
|
||||||
id: string,
|
id: string,
|
||||||
actor: OrgServiceActor
|
actor: ProjectServiceActor
|
||||||
) => Promise<{
|
) => Promise<{
|
||||||
environments: TProjectTemplateEnvironment[];
|
environments: TProjectTemplateEnvironment[];
|
||||||
roles: {
|
roles: {
|
||||||
@@ -155,7 +155,7 @@ export type TProjectTemplateServiceFactory = {
|
|||||||
}>;
|
}>;
|
||||||
findProjectTemplateById: (
|
findProjectTemplateById: (
|
||||||
id: string,
|
id: string,
|
||||||
actor: OrgServiceActor
|
actor: ProjectServiceActor
|
||||||
) => Promise<{
|
) => Promise<{
|
||||||
packedRoles: TProjectTemplateRole[];
|
packedRoles: TProjectTemplateRole[];
|
||||||
environments: TProjectTemplateEnvironment[];
|
environments: TProjectTemplateEnvironment[];
|
||||||
@@ -179,7 +179,7 @@ export type TProjectTemplateServiceFactory = {
|
|||||||
}>;
|
}>;
|
||||||
findProjectTemplateByName: (
|
findProjectTemplateByName: (
|
||||||
name: string,
|
name: string,
|
||||||
actor: OrgServiceActor
|
actor: ProjectServiceActor
|
||||||
) => Promise<{
|
) => Promise<{
|
||||||
packedRoles: TProjectTemplateRole[];
|
packedRoles: TProjectTemplateRole[];
|
||||||
environments: TProjectTemplateEnvironment[];
|
environments: TProjectTemplateEnvironment[];
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { isIP } from "node:net";
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import * as x509 from "@peculiar/x509";
|
import * as x509 from "@peculiar/x509";
|
||||||
|
|
||||||
import { OrgMembershipRole, TRelays } from "@app/db/schemas";
|
import { OrganizationActionScope, OrgMembershipRole, TRelays } from "@app/db/schemas";
|
||||||
import { PgSqlLock } from "@app/keystore/keystore";
|
import { PgSqlLock } from "@app/keystore/keystore";
|
||||||
import { crypto } from "@app/lib/crypto";
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
@@ -126,8 +126,8 @@ export const relayServiceFactory = ({
|
|||||||
|
|
||||||
// generate instance relay CA
|
// generate instance relay CA
|
||||||
const instanceRelayCaSerialNumber = createSerialNumber();
|
const instanceRelayCaSerialNumber = createSerialNumber();
|
||||||
const instanceRelayCaIssuedAt = new Date();
|
|
||||||
const instanceRelayCaExpiration = new Date(new Date().setFullYear(2045));
|
const instanceRelayCaExpiration = new Date(new Date().setFullYear(2045));
|
||||||
|
const instanceRelayCaIssuedAt = new Date();
|
||||||
const instanceRelayCaKeys = await crypto.nativeCrypto.subtle.generateKey(alg, true, ["sign", "verify"]);
|
const instanceRelayCaKeys = await crypto.nativeCrypto.subtle.generateKey(alg, true, ["sign", "verify"]);
|
||||||
const instanceRelayCaSkObj = crypto.nativeCrypto.KeyObject.from(instanceRelayCaKeys.privateKey);
|
const instanceRelayCaSkObj = crypto.nativeCrypto.KeyObject.from(instanceRelayCaKeys.privateKey);
|
||||||
const instanceRelayCaCert = await x509.X509CertificateGenerator.create({
|
const instanceRelayCaCert = await x509.X509CertificateGenerator.create({
|
||||||
@@ -972,13 +972,14 @@ export const relayServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
ActorType.IDENTITY,
|
scope: OrganizationActionScope.Any,
|
||||||
identityId,
|
actor: ActorType.IDENTITY,
|
||||||
|
actorId: identityId,
|
||||||
orgId,
|
orgId,
|
||||||
actorAuthMethod!,
|
actorAuthMethod: actorAuthMethod!,
|
||||||
orgId
|
actorOrgId: orgId
|
||||||
);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionRelayActions.CreateRelays,
|
OrgPermissionRelayActions.CreateRelays,
|
||||||
@@ -1102,13 +1103,14 @@ export const relayServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
ActorType.IDENTITY,
|
scope: OrganizationActionScope.Any,
|
||||||
identityId,
|
actor: ActorType.IDENTITY,
|
||||||
|
actorId: identityId,
|
||||||
orgId,
|
orgId,
|
||||||
actorAuthMethod!,
|
actorAuthMethod: actorAuthMethod!,
|
||||||
orgId
|
actorOrgId: orgId
|
||||||
);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
OrgPermissionRelayActions.CreateRelays,
|
OrgPermissionRelayActions.CreateRelays,
|
||||||
OrgPermissionSubjects.Relay
|
OrgPermissionSubjects.Relay
|
||||||
@@ -1155,13 +1157,14 @@ export const relayServiceFactory = ({
|
|||||||
actorAuthMethod: ActorAuthMethod;
|
actorAuthMethod: ActorAuthMethod;
|
||||||
actorOrgId: string;
|
actorOrgId: string;
|
||||||
}) => {
|
}) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod: actorAuthMethod!,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionRelayActions.ListRelays, OrgPermissionSubjects.Relay);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionRelayActions.ListRelays, OrgPermissionSubjects.Relay);
|
||||||
|
|
||||||
@@ -1189,13 +1192,14 @@ export const relayServiceFactory = ({
|
|||||||
actorAuthMethod: ActorAuthMethod;
|
actorAuthMethod: ActorAuthMethod;
|
||||||
actorOrgId: string;
|
actorOrgId: string;
|
||||||
}) => {
|
}) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
orgId: actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionRelayActions.DeleteRelays, OrgPermissionSubjects.Relay);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionRelayActions.DeleteRelays, OrgPermissionSubjects.Relay);
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import RE2 from "re2";
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
AccessScope,
|
AccessScope,
|
||||||
|
OrganizationActionScope,
|
||||||
OrgMembershipRole,
|
OrgMembershipRole,
|
||||||
OrgMembershipStatus,
|
OrgMembershipStatus,
|
||||||
TableName,
|
TableName,
|
||||||
@@ -251,7 +252,14 @@ export const samlConfigServiceFactory = ({
|
|||||||
authProvider,
|
authProvider,
|
||||||
enableGroupSync
|
enableGroupSync
|
||||||
}) => {
|
}) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.ParentOrganization,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
orgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Sso);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Sso);
|
||||||
|
|
||||||
const plan = await licenseService.getPlan(orgId);
|
const plan = await licenseService.getPlan(orgId);
|
||||||
@@ -317,7 +325,14 @@ export const samlConfigServiceFactory = ({
|
|||||||
authProvider,
|
authProvider,
|
||||||
enableGroupSync
|
enableGroupSync
|
||||||
}) => {
|
}) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.ParentOrganization,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
orgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Sso);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Sso);
|
||||||
const plan = await licenseService.getPlan(orgId);
|
const plan = await licenseService.getPlan(orgId);
|
||||||
if (!plan.samlSSO)
|
if (!plan.samlSSO)
|
||||||
@@ -393,7 +408,7 @@ export const samlConfigServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} 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, rootOrgId: null });
|
||||||
if (!org) {
|
if (!org) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Organization with slug '${dto.orgSlug}' not found`
|
message: `Organization with slug '${dto.orgSlug}' not found`
|
||||||
@@ -424,13 +439,14 @@ export const samlConfigServiceFactory = ({
|
|||||||
|
|
||||||
// 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,
|
scope: OrganizationActionScope.ParentOrganization,
|
||||||
dto.actorId,
|
actor: dto.actor,
|
||||||
samlConfig.orgId,
|
actorId: dto.actorId,
|
||||||
dto.actorAuthMethod,
|
orgId: samlConfig.orgId,
|
||||||
dto.actorOrgId
|
actorAuthMethod: dto.actorAuthMethod,
|
||||||
);
|
actorOrgId: dto.actorOrgId
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Sso);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Sso);
|
||||||
}
|
}
|
||||||
const { decryptor } = await kmsService.createCipherPairWithDataKey({
|
const { decryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export type TGetSamlCfgDTO =
|
|||||||
actor: ActorType;
|
actor: ActorType;
|
||||||
actorId: string;
|
actorId: string;
|
||||||
actorAuthMethod: ActorAuthMethod;
|
actorAuthMethod: ActorAuthMethod;
|
||||||
actorOrgId: string | undefined;
|
actorOrgId: string;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: "orgSlug";
|
type: "orgSlug";
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { scimPatch } from "scim-patch";
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
AccessScope,
|
AccessScope,
|
||||||
|
OrganizationActionScope,
|
||||||
OrgMembershipRole,
|
OrgMembershipRole,
|
||||||
OrgMembershipStatus,
|
OrgMembershipStatus,
|
||||||
TableName,
|
TableName,
|
||||||
@@ -56,6 +57,7 @@ type TScimServiceFactoryDep = {
|
|||||||
TOrgDALFactory,
|
TOrgDALFactory,
|
||||||
| "createMembership"
|
| "createMembership"
|
||||||
| "findById"
|
| "findById"
|
||||||
|
| "find"
|
||||||
| "findMembership"
|
| "findMembership"
|
||||||
| "findMembershipWithScimFilter"
|
| "findMembershipWithScimFilter"
|
||||||
| "deleteMembershipById"
|
| "deleteMembershipById"
|
||||||
@@ -125,7 +127,14 @@ export const scimServiceFactory = ({
|
|||||||
description,
|
description,
|
||||||
ttlDays
|
ttlDays
|
||||||
}) => {
|
}) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.ParentOrganization,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
orgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Scim);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Scim);
|
||||||
|
|
||||||
const plan = await licenseService.getPlan(orgId);
|
const plan = await licenseService.getPlan(orgId);
|
||||||
@@ -160,7 +169,14 @@ export const scimServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
orgId
|
orgId
|
||||||
}) => {
|
}) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.ParentOrganization,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
orgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Scim);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Scim);
|
||||||
|
|
||||||
const plan = await licenseService.getPlan(orgId);
|
const plan = await licenseService.getPlan(orgId);
|
||||||
@@ -183,13 +199,14 @@ export const scimServiceFactory = ({
|
|||||||
let scimToken = await scimDAL.findById(scimTokenId);
|
let scimToken = await scimDAL.findById(scimTokenId);
|
||||||
if (!scimToken) throw new NotFoundError({ message: `SCIM token with ID '${scimTokenId}' not found` });
|
if (!scimToken) throw new NotFoundError({ message: `SCIM token with ID '${scimTokenId}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.ParentOrganization,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
scimToken.orgId,
|
orgId: scimToken.orgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Scim);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Scim);
|
||||||
|
|
||||||
const plan = await licenseService.getPlan(scimToken.orgId);
|
const plan = await licenseService.getPlan(scimToken.orgId);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { ForbiddenError } from "@casl/ability";
|
|||||||
import { WebhookEventMap } from "@octokit/webhooks-types";
|
import { WebhookEventMap } from "@octokit/webhooks-types";
|
||||||
import { ProbotOctokit } from "probot";
|
import { ProbotOctokit } from "probot";
|
||||||
|
|
||||||
|
import { OrganizationActionScope } from "@app/db/schemas";
|
||||||
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-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
@@ -49,7 +50,14 @@ export const secretScanningServiceFactory = ({
|
|||||||
}: TInstallAppSessionDTO) => {
|
}: TInstallAppSessionDTO) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
orgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.SecretScanning);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.SecretScanning);
|
||||||
|
|
||||||
const sessionId = crypto.randomBytes(16).toString("hex");
|
const sessionId = crypto.randomBytes(16).toString("hex");
|
||||||
@@ -68,13 +76,14 @@ export const secretScanningServiceFactory = ({
|
|||||||
const session = await gitAppInstallSessionDAL.findOne({ sessionId });
|
const session = await gitAppInstallSessionDAL.findOne({ sessionId });
|
||||||
if (!session) throw new NotFoundError({ message: "Session was not found" });
|
if (!session) throw new NotFoundError({ message: "Session was not found" });
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
session.orgId,
|
orgId: session.orgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.SecretScanning);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.SecretScanning);
|
||||||
const installatedApp = await gitAppOrgDAL.transaction(async (tx) => {
|
const installatedApp = await gitAppOrgDAL.transaction(async (tx) => {
|
||||||
await gitAppInstallSessionDAL.deleteById(session.id, tx);
|
await gitAppInstallSessionDAL.deleteById(session.id, tx);
|
||||||
@@ -117,7 +126,14 @@ export const secretScanningServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
}: TGetOrgInstallStatusDTO) => {
|
}: TGetOrgInstallStatusDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
orgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning);
|
||||||
|
|
||||||
const appInstallation = await gitAppOrgDAL.findOne({ orgId });
|
const appInstallation = await gitAppOrgDAL.findOne({ orgId });
|
||||||
@@ -125,7 +141,14 @@ export const secretScanningServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getRisksByOrg = async ({ actor, orgId, actorId, actorAuthMethod, actorOrgId, filter }: TGetOrgRisksDTO) => {
|
const getRisksByOrg = async ({ actor, orgId, actorId, actorAuthMethod, actorOrgId, filter }: TGetOrgRisksDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
orgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning);
|
||||||
|
|
||||||
const results = await secretScanningDAL.findByOrgId(orgId, filter);
|
const results = await secretScanningDAL.findByOrgId(orgId, filter);
|
||||||
@@ -134,7 +157,14 @@ export const secretScanningServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getAllRisksByOrg = async ({ actor, orgId, actorId, actorAuthMethod, actorOrgId }: TGetAllOrgRisksDTO) => {
|
const getAllRisksByOrg = async ({ actor, orgId, actorId, actorAuthMethod, actorOrgId }: TGetAllOrgRisksDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
orgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning);
|
||||||
|
|
||||||
const risks = await secretScanningDAL.find({ orgId }, { sort: [["createdAt", "desc"]] });
|
const risks = await secretScanningDAL.find({ orgId }, { sort: [["createdAt", "desc"]] });
|
||||||
@@ -150,7 +180,14 @@ export const secretScanningServiceFactory = ({
|
|||||||
riskId,
|
riskId,
|
||||||
status
|
status
|
||||||
}: TUpdateRiskStatusDTO) => {
|
}: TUpdateRiskStatusDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
scope: OrganizationActionScope.Any,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
orgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.SecretScanning);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.SecretScanning);
|
||||||
|
|
||||||
const isRiskResolved = Boolean(
|
const isRiskResolved = Boolean(
|
||||||
|
|||||||
@@ -0,0 +1,160 @@
|
|||||||
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
|
import { AccessScope, OrganizationActionScope, OrgMembershipRole, OrgMembershipStatus } from "@app/db/schemas";
|
||||||
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
|
import { ActorType } from "@app/services/auth/auth-type";
|
||||||
|
import { TMembershipDALFactory } from "@app/services/membership/membership-dal";
|
||||||
|
import { TMembershipRoleDALFactory } from "@app/services/membership/membership-role-dal";
|
||||||
|
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||||
|
|
||||||
|
import { TLicenseServiceFactory } from "../license/license-service";
|
||||||
|
import { OrgPermissionActions, OrgPermissionSubjects, OrgPermissionSubOrgActions } from "../permission/org-permission";
|
||||||
|
import { TPermissionServiceFactory } from "../permission/permission-service-types";
|
||||||
|
import { TCreateSubOrgDTO, TListSubOrgDTO, TUpdateSubOrgDTO } from "./sub-org-types";
|
||||||
|
|
||||||
|
type TSubOrgServiceFactoryDep = {
|
||||||
|
orgDAL: Pick<
|
||||||
|
TOrgDALFactory,
|
||||||
|
"findOne" | "create" | "transaction" | "listSubOrganizations" | "updateById" | "findById"
|
||||||
|
>;
|
||||||
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
|
membershipDAL: Pick<TMembershipDALFactory, "create">;
|
||||||
|
membershipRoleDAL: Pick<TMembershipRoleDALFactory, "create">;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TSubOrgServiceFactory = ReturnType<typeof subOrgServiceFactory>;
|
||||||
|
|
||||||
|
export const subOrgServiceFactory = ({
|
||||||
|
orgDAL,
|
||||||
|
permissionService,
|
||||||
|
licenseService,
|
||||||
|
membershipDAL,
|
||||||
|
membershipRoleDAL
|
||||||
|
}: TSubOrgServiceFactoryDep) => {
|
||||||
|
const createSubOrg = async ({ name, permissionActor }: TCreateSubOrgDTO) => {
|
||||||
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
actorId: permissionActor.id,
|
||||||
|
actor: permissionActor.type,
|
||||||
|
orgId: permissionActor.orgId,
|
||||||
|
actorOrgId: permissionActor.orgId,
|
||||||
|
actorAuthMethod: permissionActor.authMethod,
|
||||||
|
scope: OrganizationActionScope.ParentOrganization
|
||||||
|
});
|
||||||
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
OrgPermissionSubOrgActions.Create,
|
||||||
|
OrgPermissionSubjects.SubOrganization
|
||||||
|
);
|
||||||
|
|
||||||
|
const orgLicensePlan = await licenseService.getPlan(permissionActor.rootOrgId);
|
||||||
|
if (!orgLicensePlan.subOrganization) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Sub-organization creation failed. Please upgrade your instance to Infisical's Enterprise plan."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingSubOrg = await orgDAL.findOne({
|
||||||
|
parentOrgId: permissionActor.orgId,
|
||||||
|
name
|
||||||
|
});
|
||||||
|
if (existingSubOrg) {
|
||||||
|
throw new BadRequestError({ message: `Sub-organization with name ${name} already exists` });
|
||||||
|
}
|
||||||
|
|
||||||
|
const organization = await orgDAL.transaction(async (tx) => {
|
||||||
|
const org = await orgDAL.create(
|
||||||
|
{ name, slug: name, rootOrgId: permissionActor.rootOrgId, parentOrgId: permissionActor.orgId },
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
const membership = await membershipDAL.create(
|
||||||
|
{
|
||||||
|
scope: AccessScope.Organization,
|
||||||
|
[permissionActor.type === ActorType.IDENTITY ? "actorIdentityId" : "actorUserId"]: permissionActor.id,
|
||||||
|
scopeOrgId: org.id,
|
||||||
|
status: OrgMembershipStatus.Accepted,
|
||||||
|
isActive: true
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
await membershipRoleDAL.create(
|
||||||
|
{
|
||||||
|
membershipId: membership.id,
|
||||||
|
role: OrgMembershipRole.Admin
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
return org;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
organization
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const listSubOrgs = async ({ permissionActor, data }: TListSubOrgDTO) => {
|
||||||
|
await permissionService.getOrgPermission({
|
||||||
|
actorId: permissionActor.id,
|
||||||
|
actor: permissionActor.type,
|
||||||
|
orgId: permissionActor.rootOrgId,
|
||||||
|
actorOrgId: permissionActor.rootOrgId,
|
||||||
|
actorAuthMethod: permissionActor.authMethod,
|
||||||
|
scope: OrganizationActionScope.Any
|
||||||
|
});
|
||||||
|
|
||||||
|
const organizations = await orgDAL.listSubOrganizations({
|
||||||
|
actorId: permissionActor.id,
|
||||||
|
actorType: permissionActor.type,
|
||||||
|
orgId: permissionActor.rootOrgId,
|
||||||
|
isAccessible: data?.isAccessible,
|
||||||
|
limit: data?.limit,
|
||||||
|
offset: data?.offset
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
organizations
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateSubOrg = async ({ subOrgId, name, permissionActor }: TUpdateSubOrgDTO) => {
|
||||||
|
const subOrg = await orgDAL.findOne({
|
||||||
|
rootOrgId: permissionActor.rootOrgId,
|
||||||
|
id: subOrgId
|
||||||
|
});
|
||||||
|
if (!subOrg) {
|
||||||
|
throw new BadRequestError({ message: "Sub-organization not found" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const { permission } = await permissionService.getOrgPermission({
|
||||||
|
actorId: permissionActor.id,
|
||||||
|
actor: permissionActor.type,
|
||||||
|
orgId: subOrgId,
|
||||||
|
actorOrgId: subOrgId,
|
||||||
|
actorAuthMethod: permissionActor.authMethod,
|
||||||
|
scope: OrganizationActionScope.ChildOrganization
|
||||||
|
});
|
||||||
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings);
|
||||||
|
|
||||||
|
const existingSubOrg = await orgDAL.findOne({
|
||||||
|
parentOrgId: subOrg.parentOrgId,
|
||||||
|
slug: name
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existingSubOrg && existingSubOrg.id !== subOrgId) {
|
||||||
|
throw new BadRequestError({ message: `Sub-organization with name ${name} already exists` });
|
||||||
|
}
|
||||||
|
|
||||||
|
const organization = await orgDAL.updateById(subOrgId, { name, slug: name });
|
||||||
|
|
||||||
|
return {
|
||||||
|
organization
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
createSubOrg,
|
||||||
|
listSubOrgs,
|
||||||
|
updateSubOrg
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { OrgServiceActor } from "@app/lib/types";
|
||||||
|
|
||||||
|
export type TCreateSubOrgDTO = {
|
||||||
|
name: string;
|
||||||
|
permissionActor: OrgServiceActor;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TListSubOrgDTO = {
|
||||||
|
permissionActor: OrgServiceActor;
|
||||||
|
data: Partial<{
|
||||||
|
limit?: number;
|
||||||
|
offset?: number;
|
||||||
|
search?: string;
|
||||||
|
isAccessible?: boolean;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TUpdateSubOrgDTO = {
|
||||||
|
subOrgId: string;
|
||||||
|
name: string;
|
||||||
|
permissionActor: OrgServiceActor;
|
||||||
|
};
|
||||||
@@ -33,6 +33,7 @@ export enum ApiDocsTags {
|
|||||||
LdapAuth = "LDAP Auth",
|
LdapAuth = "LDAP Auth",
|
||||||
Groups = "Groups",
|
Groups = "Groups",
|
||||||
Organizations = "Organizations",
|
Organizations = "Organizations",
|
||||||
|
SubOrganizations = "Sub Organizations",
|
||||||
Projects = "Projects",
|
Projects = "Projects",
|
||||||
ProjectUsers = "Project Users",
|
ProjectUsers = "Project Users",
|
||||||
ProjectGroups = "Project Groups",
|
ProjectGroups = "Project Groups",
|
||||||
@@ -717,6 +718,21 @@ export const ORGANIZATIONS = {
|
|||||||
}
|
}
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
export const SUB_ORGANIZATIONS = {
|
||||||
|
CREATE: {
|
||||||
|
name: "The name of the sub organization to create."
|
||||||
|
},
|
||||||
|
UPDATE: {
|
||||||
|
name: "The name of the sub organization to update.",
|
||||||
|
subOrgId: "The id of the sub organization to update."
|
||||||
|
},
|
||||||
|
LIST: {
|
||||||
|
limit: "The number of sub organizations to return.",
|
||||||
|
offset: "The offset to start from. If you enter 10, it will start from the 10th sub organization.",
|
||||||
|
isAccessible: "Filter to only return sub organizations that the actor has access to."
|
||||||
|
}
|
||||||
|
} as const;
|
||||||
|
|
||||||
export const PROJECTS = {
|
export const PROJECTS = {
|
||||||
CREATE: {
|
CREATE: {
|
||||||
organizationSlug: "The slug of the organization to create the project in.",
|
organizationSlug: "The slug of the organization to create the project in.",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { THsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
|
||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
import { QueueWorkerProfile } from "@app/lib/types";
|
import { QueueWorkerProfile } from "@app/lib/types";
|
||||||
import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
@@ -8,6 +9,7 @@ import { BadRequestError } from "../errors";
|
|||||||
import { removeTrailingSlash } from "../fn";
|
import { removeTrailingSlash } from "../fn";
|
||||||
import { CustomLogger } from "../logger/logger";
|
import { CustomLogger } from "../logger/logger";
|
||||||
import { zpStr } from "../zod";
|
import { zpStr } from "../zod";
|
||||||
|
import { TKmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
|
|
||||||
export const GITLAB_URL = "https://gitlab.com";
|
export const GITLAB_URL = "https://gitlab.com";
|
||||||
|
|
||||||
@@ -363,11 +365,6 @@ const envSchema = z
|
|||||||
/* INTERNAL ----------------------------------------------------------------------------- */
|
/* INTERNAL ----------------------------------------------------------------------------- */
|
||||||
INTERNAL_REGION: zpStr(z.enum(["us", "eu"]).optional())
|
INTERNAL_REGION: zpStr(z.enum(["us", "eu"]).optional())
|
||||||
})
|
})
|
||||||
// To ensure that basic encryption is always possible.
|
|
||||||
.refine(
|
|
||||||
(data) => Boolean(data.ENCRYPTION_KEY) || Boolean(data.ROOT_ENCRYPTION_KEY),
|
|
||||||
"Either ENCRYPTION_KEY or ROOT_ENCRYPTION_KEY must be defined."
|
|
||||||
)
|
|
||||||
.refine(
|
.refine(
|
||||||
(data) => Boolean(data.REDIS_URL) || Boolean(data.REDIS_SENTINEL_HOSTS) || Boolean(data.REDIS_CLUSTER_HOSTS),
|
(data) => Boolean(data.REDIS_URL) || Boolean(data.REDIS_SENTINEL_HOSTS) || Boolean(data.REDIS_CLUSTER_HOSTS),
|
||||||
"Either REDIS_URL, REDIS_SENTINEL_HOSTS or REDIS_CLUSTER_HOSTS must be defined."
|
"Either REDIS_URL, REDIS_SENTINEL_HOSTS or REDIS_CLUSTER_HOSTS must be defined."
|
||||||
@@ -453,7 +450,12 @@ export const getConfig = () => envCfg;
|
|||||||
export const getOriginalConfig = () => originalEnvConfig;
|
export const getOriginalConfig = () => originalEnvConfig;
|
||||||
|
|
||||||
// cannot import singleton logger directly as it needs config to load various transport
|
// cannot import singleton logger directly as it needs config to load various transport
|
||||||
export const initEnvConfig = async (superAdminDAL?: TSuperAdminDALFactory, logger?: CustomLogger) => {
|
export const initEnvConfig = async (
|
||||||
|
hsmService: THsmServiceFactory,
|
||||||
|
kmsRootConfigDAL: TKmsRootConfigDALFactory,
|
||||||
|
superAdminDAL?: TSuperAdminDALFactory,
|
||||||
|
logger?: CustomLogger
|
||||||
|
) => {
|
||||||
const parsedEnv = envSchema.safeParse(process.env);
|
const parsedEnv = envSchema.safeParse(process.env);
|
||||||
if (!parsedEnv.success) {
|
if (!parsedEnv.success) {
|
||||||
(logger ?? console).error("Invalid environment variables. Check the error below");
|
(logger ?? console).error("Invalid environment variables. Check the error below");
|
||||||
@@ -469,7 +471,7 @@ export const initEnvConfig = async (superAdminDAL?: TSuperAdminDALFactory, logge
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (superAdminDAL) {
|
if (superAdminDAL) {
|
||||||
const fipsEnabled = await crypto.initialize(superAdminDAL);
|
const fipsEnabled = await crypto.initialize(superAdminDAL, hsmService, kmsRootConfigDAL);
|
||||||
|
|
||||||
if (fipsEnabled) {
|
if (fipsEnabled) {
|
||||||
const newEnvCfg = {
|
const newEnvCfg = {
|
||||||
@@ -532,6 +534,22 @@ export const getDatabaseCredentials = (logger?: CustomLogger) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getHsmConfig = (logger?: CustomLogger) => {
|
||||||
|
const parsedEnv = envSchema.safeParse(process.env);
|
||||||
|
if (!parsedEnv.success) {
|
||||||
|
(logger ?? console).error("Invalid environment variables. Check the error below");
|
||||||
|
(logger ?? console).error(parsedEnv.error.issues);
|
||||||
|
process.exit(-1);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
isHsmConfigured: parsedEnv.data.isHsmConfigured,
|
||||||
|
HSM_PIN: parsedEnv.data.HSM_PIN,
|
||||||
|
HSM_SLOT: parsedEnv.data.HSM_SLOT,
|
||||||
|
HSM_LIB_PATH: parsedEnv.data.HSM_LIB_PATH,
|
||||||
|
HSM_KEY_LABEL: parsedEnv.data.HSM_KEY_LABEL
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// A list of environment variables that can be overwritten
|
// A list of environment variables that can be overwritten
|
||||||
export const overwriteSchema: {
|
export const overwriteSchema: {
|
||||||
[key: string]: {
|
[key: string]: {
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ import nacl from "tweetnacl";
|
|||||||
import naclUtils from "tweetnacl-util";
|
import naclUtils from "tweetnacl-util";
|
||||||
|
|
||||||
import { SecretEncryptionAlgo, SecretKeyEncoding } from "@app/db/schemas";
|
import { SecretEncryptionAlgo, SecretKeyEncoding } from "@app/db/schemas";
|
||||||
|
import { isHsmActiveAndEnabled } from "@app/ee/services/hsm/hsm-fns";
|
||||||
|
import { THsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
|
import { TKmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
|
import { RootKeyEncryptionStrategy } from "@app/services/kms/kms-types";
|
||||||
import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
import { ADMIN_CONFIG_DB_UUID } from "@app/services/super-admin/super-admin-service";
|
import { ADMIN_CONFIG_DB_UUID } from "@app/services/super-admin/super-admin-service";
|
||||||
|
|
||||||
@@ -106,13 +110,31 @@ const cryptographyFactory = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const $setFipsModeEnabled = (enabled: boolean, envCfg?: Pick<TEnvConfig, "ENCRYPTION_KEY">) => {
|
const $setFipsModeEnabled = async (
|
||||||
|
enabled: boolean,
|
||||||
|
hsmService: THsmServiceFactory,
|
||||||
|
kmsRootConfigDAL: TKmsRootConfigDALFactory,
|
||||||
|
envCfg?: Pick<TEnvConfig, "ENCRYPTION_KEY">
|
||||||
|
) => {
|
||||||
// If FIPS is enabled, we need to validate that the ENCRYPTION_KEY is in a base64 format, and is a 256-bit key.
|
// If FIPS is enabled, we need to validate that the ENCRYPTION_KEY is in a base64 format, and is a 256-bit key.
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
crypto.setFips(true);
|
crypto.setFips(true);
|
||||||
|
|
||||||
const appCfg = envCfg || getConfig();
|
const appCfg = envCfg || getConfig();
|
||||||
|
|
||||||
|
const hsmStatus = await isHsmActiveAndEnabled({
|
||||||
|
hsmService,
|
||||||
|
kmsRootConfigDAL
|
||||||
|
});
|
||||||
|
|
||||||
|
// if the encryption strategy is software - user needs to provide an encryption key
|
||||||
|
// if the encryption strategy is null AND the hsm is not configured - user needs to provide an encryption key
|
||||||
|
const needsEncryptionKey =
|
||||||
|
hsmStatus.rootKmsConfigEncryptionStrategy === RootKeyEncryptionStrategy.Software ||
|
||||||
|
(hsmStatus.rootKmsConfigEncryptionStrategy === null && !hsmStatus.isHsmConfigured);
|
||||||
|
|
||||||
|
// only perform encryption key validation if it's actually required.
|
||||||
|
if (needsEncryptionKey) {
|
||||||
if (appCfg.ENCRYPTION_KEY) {
|
if (appCfg.ENCRYPTION_KEY) {
|
||||||
// we need to validate that the ENCRYPTION_KEY is a base64 encoded 256-bit key
|
// we need to validate that the ENCRYPTION_KEY is a base64 encoded 256-bit key
|
||||||
|
|
||||||
@@ -137,18 +159,24 @@ const cryptographyFactory = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
$fipsEnabled = enabled;
|
$fipsEnabled = enabled;
|
||||||
$isInitialized = true;
|
$isInitialized = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialize = async (superAdminDAL: TSuperAdminDALFactory, envCfg?: Pick<TEnvConfig, "ENCRYPTION_KEY">) => {
|
const initialize = async (
|
||||||
|
superAdminDAL: TSuperAdminDALFactory,
|
||||||
|
hsmService: THsmServiceFactory,
|
||||||
|
kmsRootConfigDAL: TKmsRootConfigDALFactory,
|
||||||
|
envCfg?: Pick<TEnvConfig, "ENCRYPTION_KEY">
|
||||||
|
) => {
|
||||||
if ($isInitialized) {
|
if ($isInitialized) {
|
||||||
return isFipsModeEnabled();
|
return isFipsModeEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.FIPS_ENABLED !== "true") {
|
if (process.env.FIPS_ENABLED !== "true") {
|
||||||
logger.info("Cryptography module initialized in normal operation mode.");
|
logger.info("Cryptography module initialized in normal operation mode.");
|
||||||
$setFipsModeEnabled(false, envCfg);
|
await $setFipsModeEnabled(false, hsmService, kmsRootConfigDAL, envCfg);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,11 +186,11 @@ const cryptographyFactory = () => {
|
|||||||
if (serverCfg) {
|
if (serverCfg) {
|
||||||
if (serverCfg.fipsEnabled) {
|
if (serverCfg.fipsEnabled) {
|
||||||
logger.info("[FIPS]: Instance is configured for FIPS mode of operation. Continuing startup with FIPS enabled.");
|
logger.info("[FIPS]: Instance is configured for FIPS mode of operation. Continuing startup with FIPS enabled.");
|
||||||
$setFipsModeEnabled(true, envCfg);
|
await $setFipsModeEnabled(true, hsmService, kmsRootConfigDAL, envCfg);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
logger.info("[FIPS]: Instance age predates FIPS mode inception date. Continuing without FIPS.");
|
logger.info("[FIPS]: Instance age predates FIPS mode inception date. Continuing without FIPS.");
|
||||||
$setFipsModeEnabled(false, envCfg);
|
await $setFipsModeEnabled(false, hsmService, kmsRootConfigDAL, envCfg);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +199,7 @@ const cryptographyFactory = () => {
|
|||||||
// TODO(daniel): check if it's an enterprise deployment
|
// TODO(daniel): check if it's an enterprise deployment
|
||||||
|
|
||||||
// if there is no server cfg, and FIPS_MODE is `true`, its a fresh FIPS deployment. We need to set the fipsEnabled to true.
|
// if there is no server cfg, and FIPS_MODE is `true`, its a fresh FIPS deployment. We need to set the fipsEnabled to true.
|
||||||
$setFipsModeEnabled(true, envCfg);
|
await $setFipsModeEnabled(true, hsmService, kmsRootConfigDAL, envCfg);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -258,6 +286,13 @@ const cryptographyFactory = () => {
|
|||||||
const rootEncryptionKey = appCfg.ROOT_ENCRYPTION_KEY;
|
const rootEncryptionKey = appCfg.ROOT_ENCRYPTION_KEY;
|
||||||
const encryptionKey = appCfg.ENCRYPTION_KEY;
|
const encryptionKey = appCfg.ENCRYPTION_KEY;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!rootEncryptionKey && !encryptionKey) {
|
||||||
|
throw new CryptographyError({
|
||||||
|
message: "Tried to encrypt with instance root encryption key, but no root encryption key is set."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (rootEncryptionKey) {
|
if (rootEncryptionKey) {
|
||||||
const { iv, tag, ciphertext } = encrypt({
|
const { iv, tag, ciphertext } = encrypt({
|
||||||
plaintext: data,
|
plaintext: data,
|
||||||
@@ -303,6 +338,14 @@ const cryptographyFactory = () => {
|
|||||||
// the or gate is used used in migration
|
// the or gate is used used in migration
|
||||||
const rootEncryptionKey = appCfg?.ROOT_ENCRYPTION_KEY || process.env.ROOT_ENCRYPTION_KEY;
|
const rootEncryptionKey = appCfg?.ROOT_ENCRYPTION_KEY || process.env.ROOT_ENCRYPTION_KEY;
|
||||||
const encryptionKey = appCfg?.ENCRYPTION_KEY || process.env.ENCRYPTION_KEY;
|
const encryptionKey = appCfg?.ENCRYPTION_KEY || process.env.ENCRYPTION_KEY;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!rootEncryptionKey && !encryptionKey) {
|
||||||
|
throw new CryptographyError({
|
||||||
|
message: "Tried to decrypt with instance root encryption key, but no root encryption key is set."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (rootEncryptionKey && keyEncoding === SecretKeyEncoding.BASE64) {
|
if (rootEncryptionKey && keyEncoding === SecretKeyEncoding.BASE64) {
|
||||||
const data = symmetric().decrypt({
|
const data = symmetric().decrypt({
|
||||||
key: rootEncryptionKey,
|
key: rootEncryptionKey,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export type TGenericPermission = {
|
|||||||
actor: ActorType;
|
actor: ActorType;
|
||||||
actorId: string;
|
actorId: string;
|
||||||
actorAuthMethod: ActorAuthMethod;
|
actorAuthMethod: ActorAuthMethod;
|
||||||
actorOrgId: string | undefined;
|
actorOrgId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,6 +78,15 @@ export type OrgServiceActor = {
|
|||||||
id: string;
|
id: string;
|
||||||
authMethod: ActorAuthMethod;
|
authMethod: ActorAuthMethod;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
|
rootOrgId: string;
|
||||||
|
parentOrgId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ProjectServiceActor = {
|
||||||
|
type: ActorType;
|
||||||
|
id: string;
|
||||||
|
authMethod: ActorAuthMethod;
|
||||||
|
orgId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum QueueWorkerProfile {
|
export enum QueueWorkerProfile {
|
||||||
|
|||||||
+19
-6
@@ -9,14 +9,16 @@ import { keyValueStoreDALFactory } from "@app/keystore/key-value-store-dal";
|
|||||||
|
|
||||||
import { runMigrations } from "./auto-start-migrations";
|
import { runMigrations } from "./auto-start-migrations";
|
||||||
import { initAuditLogDbConnection, initDbConnection } from "./db";
|
import { initAuditLogDbConnection, initDbConnection } from "./db";
|
||||||
|
import { hsmServiceFactory } from "./ee/services/hsm/hsm-service";
|
||||||
import { keyStoreFactory } from "./keystore/keystore";
|
import { keyStoreFactory } from "./keystore/keystore";
|
||||||
import { formatSmtpConfig, getDatabaseCredentials, initEnvConfig } from "./lib/config/env";
|
import { formatSmtpConfig, getDatabaseCredentials, getHsmConfig, initEnvConfig } from "./lib/config/env";
|
||||||
import { buildRedisFromConfig } from "./lib/config/redis";
|
import { buildRedisFromConfig } from "./lib/config/redis";
|
||||||
import { removeTemporaryBaseDirectory } from "./lib/files";
|
import { removeTemporaryBaseDirectory } from "./lib/files";
|
||||||
import { initLogger } from "./lib/logger";
|
import { initLogger } from "./lib/logger";
|
||||||
import { queueServiceFactory } from "./queue";
|
import { queueServiceFactory } from "./queue";
|
||||||
import { main } from "./server/app";
|
import { main } from "./server/app";
|
||||||
import { bootstrapCheck } from "./server/boot-strap-check";
|
import { bootstrapCheck } from "./server/boot-strap-check";
|
||||||
|
import { kmsRootConfigDALFactory } from "./services/kms/kms-root-config-dal";
|
||||||
import { smtpServiceFactory } from "./services/smtp/smtp-service";
|
import { smtpServiceFactory } from "./services/smtp/smtp-service";
|
||||||
import { superAdminDALFactory } from "./services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "./services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
@@ -26,6 +28,18 @@ const run = async () => {
|
|||||||
const logger = initLogger();
|
const logger = initLogger();
|
||||||
await removeTemporaryBaseDirectory();
|
await removeTemporaryBaseDirectory();
|
||||||
|
|
||||||
|
const hsmConfig = getHsmConfig(logger);
|
||||||
|
|
||||||
|
const hsmModule = initializeHsmModule(hsmConfig);
|
||||||
|
hsmModule.initialize();
|
||||||
|
|
||||||
|
const hsmService = hsmServiceFactory({
|
||||||
|
hsmModule: hsmModule.getModule(),
|
||||||
|
envConfig: hsmConfig
|
||||||
|
});
|
||||||
|
|
||||||
|
await hsmService.startService();
|
||||||
|
|
||||||
const databaseCredentials = getDatabaseCredentials(logger);
|
const databaseCredentials = getDatabaseCredentials(logger);
|
||||||
|
|
||||||
const db = initDbConnection({
|
const db = initDbConnection({
|
||||||
@@ -35,7 +49,8 @@ const run = async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const superAdminDAL = superAdminDALFactory(db);
|
const superAdminDAL = superAdminDALFactory(db);
|
||||||
const envConfig = await initEnvConfig(superAdminDAL, logger);
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(db);
|
||||||
|
const envConfig = await initEnvConfig(hsmService, kmsRootConfigDAL, superAdminDAL, logger);
|
||||||
|
|
||||||
const auditLogDb = envConfig.AUDIT_LOGS_DB_CONNECTION_URI
|
const auditLogDb = envConfig.AUDIT_LOGS_DB_CONNECTION_URI
|
||||||
? initAuditLogDbConnection({
|
? initAuditLogDbConnection({
|
||||||
@@ -59,14 +74,12 @@ const run = async () => {
|
|||||||
const keyStore = keyStoreFactory(envConfig, keyValueStoreDAL);
|
const keyStore = keyStoreFactory(envConfig, keyValueStoreDAL);
|
||||||
const redis = buildRedisFromConfig(envConfig);
|
const redis = buildRedisFromConfig(envConfig);
|
||||||
|
|
||||||
const hsmModule = initializeHsmModule(envConfig);
|
|
||||||
hsmModule.initialize();
|
|
||||||
|
|
||||||
const server = await main({
|
const server = await main({
|
||||||
db,
|
db,
|
||||||
auditLogDb,
|
auditLogDb,
|
||||||
superAdminDAL,
|
superAdminDAL,
|
||||||
hsmModule: hsmModule.getModule(),
|
kmsRootConfigDAL,
|
||||||
|
hsmService,
|
||||||
smtp,
|
smtp,
|
||||||
logger,
|
logger,
|
||||||
queue,
|
queue,
|
||||||
|
|||||||
@@ -77,7 +77,8 @@ export enum QueueName {
|
|||||||
DailyReminders = "daily-reminders",
|
DailyReminders = "daily-reminders",
|
||||||
SecretReminderMigration = "secret-reminder-migration",
|
SecretReminderMigration = "secret-reminder-migration",
|
||||||
UserNotification = "user-notification",
|
UserNotification = "user-notification",
|
||||||
HealthAlert = "health-alert"
|
HealthAlert = "health-alert",
|
||||||
|
PamAccountRotation = "pam-account-rotation"
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum QueueJobs {
|
export enum QueueJobs {
|
||||||
@@ -126,7 +127,8 @@ export enum QueueJobs {
|
|||||||
DailyReminders = "daily-reminders",
|
DailyReminders = "daily-reminders",
|
||||||
SecretReminderMigration = "secret-reminder-migration",
|
SecretReminderMigration = "secret-reminder-migration",
|
||||||
UserNotification = "user-notification-job",
|
UserNotification = "user-notification-job",
|
||||||
HealthAlert = "health-alert"
|
HealthAlert = "health-alert",
|
||||||
|
PamAccountRotation = "pam-account-rotation"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TQueueJobTypes = {
|
export type TQueueJobTypes = {
|
||||||
@@ -357,6 +359,10 @@ export type TQueueJobTypes = {
|
|||||||
name: QueueJobs.HealthAlert;
|
name: QueueJobs.HealthAlert;
|
||||||
payload: undefined;
|
payload: undefined;
|
||||||
};
|
};
|
||||||
|
[QueueName.PamAccountRotation]: {
|
||||||
|
name: QueueJobs.PamAccountRotation;
|
||||||
|
payload: undefined;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const SECRET_SCANNING_JOBS = [
|
const SECRET_SCANNING_JOBS = [
|
||||||
|
|||||||
@@ -15,12 +15,13 @@ import fastify from "fastify";
|
|||||||
import { Cluster, Redis } from "ioredis";
|
import { Cluster, Redis } from "ioredis";
|
||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { HsmModule } from "@app/ee/services/hsm/hsm-types";
|
import { THsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
|
||||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||||
import { getConfig, IS_PACKAGED, TEnvConfig } from "@app/lib/config/env";
|
import { getConfig, IS_PACKAGED, TEnvConfig } from "@app/lib/config/env";
|
||||||
import { CustomLogger } from "@app/lib/logger/logger";
|
import { CustomLogger } from "@app/lib/logger/logger";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { TQueueServiceFactory } from "@app/queue";
|
import { TQueueServiceFactory } from "@app/queue";
|
||||||
|
import { TKmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
import { TSmtpService } from "@app/services/smtp/smtp-service";
|
import { TSmtpService } from "@app/services/smtp/smtp-service";
|
||||||
import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
@@ -42,16 +43,16 @@ type TMain = {
|
|||||||
logger?: CustomLogger;
|
logger?: CustomLogger;
|
||||||
queue: TQueueServiceFactory;
|
queue: TQueueServiceFactory;
|
||||||
keyStore: TKeyStoreFactory;
|
keyStore: TKeyStoreFactory;
|
||||||
hsmModule: HsmModule;
|
|
||||||
redis: Redis | Cluster;
|
redis: Redis | Cluster;
|
||||||
envConfig: TEnvConfig;
|
envConfig: TEnvConfig;
|
||||||
superAdminDAL: TSuperAdminDALFactory;
|
superAdminDAL: TSuperAdminDALFactory;
|
||||||
|
hsmService: THsmServiceFactory;
|
||||||
|
kmsRootConfigDAL: TKmsRootConfigDALFactory;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Run the server!
|
// Run the server!
|
||||||
export const main = async ({
|
export const main = async ({
|
||||||
db,
|
db,
|
||||||
hsmModule,
|
|
||||||
auditLogDb,
|
auditLogDb,
|
||||||
smtp,
|
smtp,
|
||||||
logger,
|
logger,
|
||||||
@@ -59,7 +60,9 @@ export const main = async ({
|
|||||||
keyStore,
|
keyStore,
|
||||||
redis,
|
redis,
|
||||||
envConfig,
|
envConfig,
|
||||||
superAdminDAL
|
superAdminDAL,
|
||||||
|
hsmService,
|
||||||
|
kmsRootConfigDAL
|
||||||
}: TMain) => {
|
}: TMain) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
|
|
||||||
@@ -148,9 +151,10 @@ export const main = async ({
|
|||||||
db,
|
db,
|
||||||
auditLogDb,
|
auditLogDb,
|
||||||
keyStore,
|
keyStore,
|
||||||
hsmModule,
|
hsmService,
|
||||||
envConfig,
|
envConfig,
|
||||||
superAdminDAL
|
superAdminDAL,
|
||||||
|
kmsRootConfigDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.register(registerServeUI, {
|
await server.register(registerServeUI, {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { TScimTokenJwtPayload } from "@app/ee/services/scim/scim-types";
|
|||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { crypto } from "@app/lib/crypto";
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
|
import { slugSchema } from "@app/server/lib/schemas";
|
||||||
import { ActorType, AuthMethod, AuthMode, AuthModeJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type";
|
import { ActorType, AuthMethod, AuthMode, AuthModeJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type";
|
||||||
import { TIdentityAccessTokenJwtPayload } from "@app/services/identity-access-token/identity-access-token-types";
|
import { TIdentityAccessTokenJwtPayload } from "@app/services/identity-access-token/identity-access-token-types";
|
||||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
||||||
@@ -20,6 +21,8 @@ export type TAuthMode =
|
|||||||
tokenVersionId: string; // the session id of token used
|
tokenVersionId: string; // the session id of token used
|
||||||
user: TUsers;
|
user: TUsers;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
|
rootOrgId: string;
|
||||||
|
parentOrgId: string;
|
||||||
authMethod: AuthMethod;
|
authMethod: AuthMethod;
|
||||||
isMfaVerified?: boolean;
|
isMfaVerified?: boolean;
|
||||||
token: AuthModeJwtTokenPayload;
|
token: AuthModeJwtTokenPayload;
|
||||||
@@ -31,6 +34,8 @@ export type TAuthMode =
|
|||||||
userId: string;
|
userId: string;
|
||||||
user: TUsers;
|
user: TUsers;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
|
rootOrgId: string;
|
||||||
|
parentOrgId: string;
|
||||||
token: string;
|
token: string;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
@@ -39,6 +44,8 @@ export type TAuthMode =
|
|||||||
actor: ActorType.SERVICE;
|
actor: ActorType.SERVICE;
|
||||||
serviceTokenId: string;
|
serviceTokenId: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
|
rootOrgId: string;
|
||||||
|
parentOrgId: string;
|
||||||
authMethod: null;
|
authMethod: null;
|
||||||
token: string;
|
token: string;
|
||||||
}
|
}
|
||||||
@@ -48,6 +55,8 @@ export type TAuthMode =
|
|||||||
identityId: string;
|
identityId: string;
|
||||||
identityName: string;
|
identityName: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
|
rootOrgId: string;
|
||||||
|
parentOrgId: string;
|
||||||
authMethod: null;
|
authMethod: null;
|
||||||
isInstanceAdmin?: boolean;
|
isInstanceAdmin?: boolean;
|
||||||
token: TIdentityAccessTokenJwtPayload;
|
token: TIdentityAccessTokenJwtPayload;
|
||||||
@@ -57,6 +66,8 @@ export type TAuthMode =
|
|||||||
actor: ActorType.SCIM_CLIENT;
|
actor: ActorType.SCIM_CLIENT;
|
||||||
scimTokenId: string;
|
scimTokenId: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
|
rootOrgId: string;
|
||||||
|
parentOrgId: string;
|
||||||
authMethod: null;
|
authMethod: null;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -136,17 +147,26 @@ export const injectIdentity = fp(
|
|||||||
|
|
||||||
if (!authMode) return;
|
if (!authMode) return;
|
||||||
|
|
||||||
|
const subOrganizationSelector = req.headers?.["x-infisical-org"] as string | undefined;
|
||||||
|
if (subOrganizationSelector) {
|
||||||
|
await slugSchema().parseAsync(subOrganizationSelector);
|
||||||
|
}
|
||||||
|
|
||||||
switch (authMode) {
|
switch (authMode) {
|
||||||
case AuthMode.JWT: {
|
case AuthMode.JWT: {
|
||||||
const { user, tokenVersionId, orgId } = await server.services.authToken.fnValidateJwtIdentity(token);
|
const { user, tokenVersionId, orgId, rootOrgId, parentOrgId } =
|
||||||
|
await server.services.authToken.fnValidateJwtIdentity(token, subOrganizationSelector);
|
||||||
requestContext.set("orgId", orgId);
|
requestContext.set("orgId", orgId);
|
||||||
|
|
||||||
req.auth = {
|
req.auth = {
|
||||||
authMode: AuthMode.JWT,
|
authMode: AuthMode.JWT,
|
||||||
user,
|
user,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
tokenVersionId,
|
tokenVersionId,
|
||||||
actor,
|
actor,
|
||||||
orgId: orgId as string,
|
orgId,
|
||||||
|
rootOrgId,
|
||||||
|
parentOrgId,
|
||||||
authMethod: token.authMethod,
|
authMethod: token.authMethod,
|
||||||
isMfaVerified: token.isMfaVerified,
|
isMfaVerified: token.isMfaVerified,
|
||||||
token
|
token
|
||||||
@@ -154,13 +174,19 @@ export const injectIdentity = fp(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AuthMode.IDENTITY_ACCESS_TOKEN: {
|
case AuthMode.IDENTITY_ACCESS_TOKEN: {
|
||||||
const identity = await server.services.identityAccessToken.fnValidateIdentityAccessToken(token, req.realIp);
|
const identity = await server.services.identityAccessToken.fnValidateIdentityAccessToken(
|
||||||
|
token,
|
||||||
|
subOrganizationSelector,
|
||||||
|
req.realIp
|
||||||
|
);
|
||||||
const serverCfg = await getServerCfg();
|
const serverCfg = await getServerCfg();
|
||||||
requestContext.set("orgId", identity.orgId);
|
requestContext.set("orgId", identity.orgId);
|
||||||
req.auth = {
|
req.auth = {
|
||||||
authMode: AuthMode.IDENTITY_ACCESS_TOKEN,
|
authMode: AuthMode.IDENTITY_ACCESS_TOKEN,
|
||||||
actor,
|
actor,
|
||||||
orgId: identity.orgId,
|
orgId: identity.orgId,
|
||||||
|
rootOrgId: identity.rootOrgId,
|
||||||
|
parentOrgId: identity.parentOrgId,
|
||||||
identityId: identity.identityId,
|
identityId: identity.identityId,
|
||||||
identityName: identity.name,
|
identityName: identity.name,
|
||||||
authMethod: null,
|
authMethod: null,
|
||||||
@@ -190,8 +216,14 @@ export const injectIdentity = fp(
|
|||||||
case AuthMode.SERVICE_TOKEN: {
|
case AuthMode.SERVICE_TOKEN: {
|
||||||
const serviceToken = await server.services.serviceToken.fnValidateServiceToken(token);
|
const serviceToken = await server.services.serviceToken.fnValidateServiceToken(token);
|
||||||
requestContext.set("orgId", serviceToken.orgId);
|
requestContext.set("orgId", serviceToken.orgId);
|
||||||
|
|
||||||
|
if (subOrganizationSelector)
|
||||||
|
throw new BadRequestError({ message: `Service token doesn't support sub organization selector` });
|
||||||
|
|
||||||
req.auth = {
|
req.auth = {
|
||||||
orgId: serviceToken.orgId,
|
orgId: serviceToken.orgId,
|
||||||
|
rootOrgId: serviceToken.rootOrgId,
|
||||||
|
parentOrgId: serviceToken.parentOrgId,
|
||||||
authMode: AuthMode.SERVICE_TOKEN as const,
|
authMode: AuthMode.SERVICE_TOKEN as const,
|
||||||
serviceToken,
|
serviceToken,
|
||||||
serviceTokenId: serviceToken.id,
|
serviceTokenId: serviceToken.id,
|
||||||
@@ -202,22 +234,27 @@ export const injectIdentity = fp(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AuthMode.API_KEY: {
|
case AuthMode.API_KEY: {
|
||||||
const user = await server.services.apiKey.fnValidateApiKey(token as string);
|
throw new BadRequestError({
|
||||||
req.auth = {
|
message: "API key authentication is not supported anymore. Please switch to identity authentication."
|
||||||
authMode: AuthMode.API_KEY as const,
|
});
|
||||||
userId: user.id,
|
|
||||||
actor,
|
|
||||||
user,
|
|
||||||
orgId: "API_KEY", // We set the orgId to an arbitrary value, since we can't link an API key to a specific org. We have to deprecate API keys soon!
|
|
||||||
authMethod: null,
|
|
||||||
token: token as string
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case AuthMode.SCIM_TOKEN: {
|
case AuthMode.SCIM_TOKEN: {
|
||||||
const { orgId, scimTokenId } = await server.services.scim.fnValidateScimToken(token);
|
const { orgId, scimTokenId } = await server.services.scim.fnValidateScimToken(token);
|
||||||
requestContext.set("orgId", orgId);
|
requestContext.set("orgId", orgId);
|
||||||
req.auth = { authMode: AuthMode.SCIM_TOKEN, actor, scimTokenId, orgId, authMethod: null };
|
|
||||||
|
if (subOrganizationSelector)
|
||||||
|
throw new BadRequestError({ message: `SCIM token doesn't support sub organization selector` });
|
||||||
|
|
||||||
|
req.auth = {
|
||||||
|
authMode: AuthMode.SCIM_TOKEN,
|
||||||
|
actor,
|
||||||
|
scimTokenId,
|
||||||
|
orgId,
|
||||||
|
authMethod: null,
|
||||||
|
// scim cannot be done for sub organization
|
||||||
|
rootOrgId: orgId,
|
||||||
|
parentOrgId: orgId
|
||||||
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ export const injectPermission = fp(async (server) => {
|
|||||||
type: ActorType.USER,
|
type: ActorType.USER,
|
||||||
id: req.auth.userId,
|
id: req.auth.userId,
|
||||||
orgId: req.auth.orgId, // if the req.auth.authMode is AuthMode.API_KEY, the orgId will be "API_KEY"
|
orgId: req.auth.orgId, // if the req.auth.authMode is AuthMode.API_KEY, the orgId will be "API_KEY"
|
||||||
authMethod: req.auth.authMethod // if the req.auth.authMode is AuthMode.API_KEY, the authMethod will be null
|
authMethod: req.auth.authMethod, // if the req.auth.authMode is AuthMode.API_KEY, the authMethod will be null
|
||||||
|
rootOrgId: req.auth.rootOrgId,
|
||||||
|
parentOrgId: req.auth.parentOrgId
|
||||||
};
|
};
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
@@ -25,7 +27,9 @@ export const injectPermission = fp(async (server) => {
|
|||||||
type: ActorType.IDENTITY,
|
type: ActorType.IDENTITY,
|
||||||
id: req.auth.identityId,
|
id: req.auth.identityId,
|
||||||
orgId: req.auth.orgId,
|
orgId: req.auth.orgId,
|
||||||
authMethod: null
|
authMethod: null,
|
||||||
|
rootOrgId: req.auth.rootOrgId,
|
||||||
|
parentOrgId: req.auth.parentOrgId
|
||||||
};
|
};
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
@@ -36,6 +40,8 @@ export const injectPermission = fp(async (server) => {
|
|||||||
type: ActorType.SERVICE,
|
type: ActorType.SERVICE,
|
||||||
id: req.auth.serviceTokenId,
|
id: req.auth.serviceTokenId,
|
||||||
orgId: req.auth.orgId,
|
orgId: req.auth.orgId,
|
||||||
|
rootOrgId: req.auth.rootOrgId,
|
||||||
|
parentOrgId: req.auth.parentOrgId,
|
||||||
authMethod: null
|
authMethod: null
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -47,6 +53,8 @@ export const injectPermission = fp(async (server) => {
|
|||||||
type: ActorType.SCIM_CLIENT,
|
type: ActorType.SCIM_CLIENT,
|
||||||
id: req.auth.scimTokenId,
|
id: req.auth.scimTokenId,
|
||||||
orgId: req.auth.orgId,
|
orgId: req.auth.orgId,
|
||||||
|
rootOrgId: req.auth.rootOrgId,
|
||||||
|
parentOrgId: req.auth.parentOrgId,
|
||||||
authMethod: null
|
authMethod: null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ import { githubOrgSyncServiceFactory } from "@app/ee/services/github-org-sync/gi
|
|||||||
import { groupDALFactory } from "@app/ee/services/group/group-dal";
|
import { groupDALFactory } from "@app/ee/services/group/group-dal";
|
||||||
import { groupServiceFactory } from "@app/ee/services/group/group-service";
|
import { groupServiceFactory } from "@app/ee/services/group/group-service";
|
||||||
import { userGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
|
import { userGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
|
||||||
import { hsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
|
import { isHsmActiveAndEnabled } from "@app/ee/services/hsm/hsm-fns";
|
||||||
import { HsmModule } from "@app/ee/services/hsm/hsm-types";
|
import { THsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
|
||||||
import { identityAuthTemplateDALFactory } from "@app/ee/services/identity-auth-template/identity-auth-template-dal";
|
import { identityAuthTemplateDALFactory } from "@app/ee/services/identity-auth-template/identity-auth-template-dal";
|
||||||
import { identityAuthTemplateServiceFactory } from "@app/ee/services/identity-auth-template/identity-auth-template-service";
|
import { identityAuthTemplateServiceFactory } from "@app/ee/services/identity-auth-template/identity-auth-template-service";
|
||||||
import { kmipClientCertificateDALFactory } from "@app/ee/services/kmip/kmip-client-certificate-dal";
|
import { kmipClientCertificateDALFactory } from "@app/ee/services/kmip/kmip-client-certificate-dal";
|
||||||
@@ -131,12 +131,14 @@ import { sshHostLoginUserDALFactory } from "@app/ee/services/ssh-host/ssh-login-
|
|||||||
import { sshHostGroupDALFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-dal";
|
import { sshHostGroupDALFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-dal";
|
||||||
import { sshHostGroupMembershipDALFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-membership-dal";
|
import { sshHostGroupMembershipDALFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-membership-dal";
|
||||||
import { sshHostGroupServiceFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-service";
|
import { sshHostGroupServiceFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-service";
|
||||||
|
import { subOrgServiceFactory } from "@app/ee/services/sub-org/sub-org-service";
|
||||||
import { trustedIpDALFactory } from "@app/ee/services/trusted-ip/trusted-ip-dal";
|
import { trustedIpDALFactory } from "@app/ee/services/trusted-ip/trusted-ip-dal";
|
||||||
import { trustedIpServiceFactory } from "@app/ee/services/trusted-ip/trusted-ip-service";
|
import { trustedIpServiceFactory } from "@app/ee/services/trusted-ip/trusted-ip-service";
|
||||||
import { keyValueStoreDALFactory } from "@app/keystore/key-value-store-dal";
|
import { keyValueStoreDALFactory } from "@app/keystore/key-value-store-dal";
|
||||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||||
import { getConfig, TEnvConfig } from "@app/lib/config/env";
|
import { getConfig, TEnvConfig } from "@app/lib/config/env";
|
||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { TQueueServiceFactory } from "@app/queue";
|
import { TQueueServiceFactory } from "@app/queue";
|
||||||
import { readLimit } from "@app/server/config/rateLimiter";
|
import { readLimit } from "@app/server/config/rateLimiter";
|
||||||
@@ -235,8 +237,9 @@ import { integrationAuthDALFactory } from "@app/services/integration-auth/integr
|
|||||||
import { integrationAuthServiceFactory } from "@app/services/integration-auth/integration-auth-service";
|
import { integrationAuthServiceFactory } from "@app/services/integration-auth/integration-auth-service";
|
||||||
import { internalKmsDALFactory } from "@app/services/kms/internal-kms-dal";
|
import { internalKmsDALFactory } from "@app/services/kms/internal-kms-dal";
|
||||||
import { kmskeyDALFactory } from "@app/services/kms/kms-key-dal";
|
import { kmskeyDALFactory } from "@app/services/kms/kms-key-dal";
|
||||||
import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
import { TKmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
import { kmsServiceFactory } from "@app/services/kms/kms-service";
|
import { kmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
|
import { RootKeyEncryptionStrategy } from "@app/services/kms/kms-types";
|
||||||
import { membershipDALFactory } from "@app/services/membership/membership-dal";
|
import { membershipDALFactory } from "@app/services/membership/membership-dal";
|
||||||
import { membershipRoleDALFactory } from "@app/services/membership/membership-role-dal";
|
import { membershipRoleDALFactory } from "@app/services/membership/membership-role-dal";
|
||||||
import { membershipGroupDALFactory } from "@app/services/membership-group/membership-group-dal";
|
import { membershipGroupDALFactory } from "@app/services/membership-group/membership-group-dal";
|
||||||
@@ -254,11 +257,11 @@ import { userNotificationDALFactory } from "@app/services/notification/user-noti
|
|||||||
import { offlineUsageReportDALFactory } from "@app/services/offline-usage-report/offline-usage-report-dal";
|
import { offlineUsageReportDALFactory } from "@app/services/offline-usage-report/offline-usage-report-dal";
|
||||||
import { offlineUsageReportServiceFactory } from "@app/services/offline-usage-report/offline-usage-report-service";
|
import { offlineUsageReportServiceFactory } from "@app/services/offline-usage-report/offline-usage-report-service";
|
||||||
import { incidentContactDALFactory } from "@app/services/org/incident-contacts-dal";
|
import { incidentContactDALFactory } from "@app/services/org/incident-contacts-dal";
|
||||||
import { orgBotDALFactory } from "@app/services/org/org-bot-dal";
|
|
||||||
import { orgDALFactory } from "@app/services/org/org-dal";
|
import { orgDALFactory } from "@app/services/org/org-dal";
|
||||||
import { orgServiceFactory } from "@app/services/org/org-service";
|
import { orgServiceFactory } from "@app/services/org/org-service";
|
||||||
import { orgAdminServiceFactory } from "@app/services/org-admin/org-admin-service";
|
import { orgAdminServiceFactory } from "@app/services/org-admin/org-admin-service";
|
||||||
import { orgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
import { orgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
||||||
|
import { pamAccountRotationServiceFactory } from "@app/services/pam-account-rotation/pam-account-rotation-queue";
|
||||||
import { dailyExpiringPkiItemAlertQueueServiceFactory } from "@app/services/pki-alert/expiring-pki-item-alert-queue";
|
import { dailyExpiringPkiItemAlertQueueServiceFactory } from "@app/services/pki-alert/expiring-pki-item-alert-queue";
|
||||||
import { pkiAlertDALFactory } from "@app/services/pki-alert/pki-alert-dal";
|
import { pkiAlertDALFactory } from "@app/services/pki-alert/pki-alert-dal";
|
||||||
import { pkiAlertServiceFactory } from "@app/services/pki-alert/pki-alert-service";
|
import { pkiAlertServiceFactory } from "@app/services/pki-alert/pki-alert-service";
|
||||||
@@ -363,20 +366,22 @@ export const registerRoutes = async (
|
|||||||
auditLogDb,
|
auditLogDb,
|
||||||
superAdminDAL,
|
superAdminDAL,
|
||||||
db,
|
db,
|
||||||
hsmModule,
|
|
||||||
smtp: smtpService,
|
smtp: smtpService,
|
||||||
queue: queueService,
|
queue: queueService,
|
||||||
keyStore,
|
keyStore,
|
||||||
envConfig
|
envConfig,
|
||||||
|
hsmService,
|
||||||
|
kmsRootConfigDAL
|
||||||
}: {
|
}: {
|
||||||
auditLogDb?: Knex;
|
auditLogDb?: Knex;
|
||||||
superAdminDAL: TSuperAdminDALFactory;
|
superAdminDAL: TSuperAdminDALFactory;
|
||||||
db: Knex;
|
db: Knex;
|
||||||
hsmModule: HsmModule;
|
|
||||||
smtp: TSmtpService;
|
smtp: TSmtpService;
|
||||||
queue: TQueueServiceFactory;
|
queue: TQueueServiceFactory;
|
||||||
keyStore: TKeyStoreFactory;
|
keyStore: TKeyStoreFactory;
|
||||||
envConfig: TEnvConfig;
|
envConfig: TEnvConfig;
|
||||||
|
hsmService: THsmServiceFactory;
|
||||||
|
kmsRootConfigDAL: TKmsRootConfigDALFactory;
|
||||||
}
|
}
|
||||||
) => {
|
) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
@@ -391,7 +396,6 @@ export const registerRoutes = async (
|
|||||||
const authTokenDAL = tokenDALFactory(db);
|
const authTokenDAL = tokenDALFactory(db);
|
||||||
const orgDAL = orgDALFactory(db);
|
const orgDAL = orgDALFactory(db);
|
||||||
const orgMembershipDAL = orgMembershipDALFactory(db);
|
const orgMembershipDAL = orgMembershipDALFactory(db);
|
||||||
const orgBotDAL = orgBotDALFactory(db);
|
|
||||||
const incidentContactDAL = incidentContactDALFactory(db);
|
const incidentContactDAL = incidentContactDALFactory(db);
|
||||||
const rateLimitDAL = rateLimitDALFactory(db);
|
const rateLimitDAL = rateLimitDALFactory(db);
|
||||||
const apiKeyDAL = apiKeyDALFactory(db);
|
const apiKeyDAL = apiKeyDALFactory(db);
|
||||||
@@ -508,7 +512,6 @@ export const registerRoutes = async (
|
|||||||
const kmsDAL = kmskeyDALFactory(db);
|
const kmsDAL = kmskeyDALFactory(db);
|
||||||
const internalKmsDAL = internalKmsDALFactory(db);
|
const internalKmsDAL = internalKmsDALFactory(db);
|
||||||
const externalKmsDAL = externalKmsDALFactory(db);
|
const externalKmsDAL = externalKmsDALFactory(db);
|
||||||
const kmsRootConfigDAL = kmsRootConfigDALFactory(db);
|
|
||||||
|
|
||||||
const slackIntegrationDAL = slackIntegrationDALFactory(db);
|
const slackIntegrationDAL = slackIntegrationDALFactory(db);
|
||||||
const projectSlackConfigDAL = projectSlackConfigDALFactory(db);
|
const projectSlackConfigDAL = projectSlackConfigDALFactory(db);
|
||||||
@@ -568,11 +571,11 @@ export const registerRoutes = async (
|
|||||||
orgDAL,
|
orgDAL,
|
||||||
licenseDAL,
|
licenseDAL,
|
||||||
keyStore,
|
keyStore,
|
||||||
identityOrgMembershipDAL,
|
projectDAL,
|
||||||
projectDAL
|
envConfig
|
||||||
});
|
});
|
||||||
|
|
||||||
const tokenService = tokenServiceFactory({ tokenDAL: authTokenDAL, userDAL, membershipUserDAL });
|
const tokenService = tokenServiceFactory({ tokenDAL: authTokenDAL, userDAL, membershipUserDAL, orgDAL });
|
||||||
|
|
||||||
const membershipUserService = membershipUserServiceFactory({
|
const membershipUserService = membershipUserServiceFactory({
|
||||||
licenseService,
|
licenseService,
|
||||||
@@ -592,6 +595,7 @@ export const registerRoutes = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
const membershipIdentityService = membershipIdentityServiceFactory({
|
const membershipIdentityService = membershipIdentityServiceFactory({
|
||||||
|
identityDAL,
|
||||||
membershipIdentityDAL,
|
membershipIdentityDAL,
|
||||||
membershipRoleDAL,
|
membershipRoleDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
@@ -623,11 +627,6 @@ export const registerRoutes = async (
|
|||||||
permissionService
|
permissionService
|
||||||
});
|
});
|
||||||
|
|
||||||
const hsmService = hsmServiceFactory({
|
|
||||||
hsmModule,
|
|
||||||
envConfig
|
|
||||||
});
|
|
||||||
|
|
||||||
const kmsService = kmsServiceFactory({
|
const kmsService = kmsServiceFactory({
|
||||||
kmsRootConfigDAL,
|
kmsRootConfigDAL,
|
||||||
keyStore,
|
keyStore,
|
||||||
@@ -900,7 +899,6 @@ export const registerRoutes = async (
|
|||||||
smtpService,
|
smtpService,
|
||||||
userDAL,
|
userDAL,
|
||||||
groupDAL,
|
groupDAL,
|
||||||
orgBotDAL,
|
|
||||||
oidcConfigDAL,
|
oidcConfigDAL,
|
||||||
ldapConfigDAL,
|
ldapConfigDAL,
|
||||||
loginService,
|
loginService,
|
||||||
@@ -912,6 +910,15 @@ export const registerRoutes = async (
|
|||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
additionalPrivilegeDAL
|
additionalPrivilegeDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const subOrgService = subOrgServiceFactory({
|
||||||
|
licenseService,
|
||||||
|
membershipDAL,
|
||||||
|
membershipRoleDAL,
|
||||||
|
orgDAL,
|
||||||
|
permissionService
|
||||||
|
});
|
||||||
|
|
||||||
const signupService = authSignupServiceFactory({
|
const signupService = authSignupServiceFactory({
|
||||||
tokenService,
|
tokenService,
|
||||||
smtpService,
|
smtpService,
|
||||||
@@ -1594,10 +1601,12 @@ export const registerRoutes = async (
|
|||||||
permissionService,
|
permissionService,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
accessTokenQueue,
|
accessTokenQueue,
|
||||||
smtpService
|
smtpService,
|
||||||
|
orgDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const identityService = identityServiceFactory({
|
const identityService = identityServiceFactory({
|
||||||
|
additionalPrivilegeDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
identityDAL,
|
identityDAL,
|
||||||
identityOrgMembershipDAL,
|
identityOrgMembershipDAL,
|
||||||
@@ -1628,10 +1637,12 @@ export const registerRoutes = async (
|
|||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
accessTokenQueue,
|
accessTokenQueue,
|
||||||
identityDAL,
|
identityDAL,
|
||||||
membershipIdentityDAL
|
membershipIdentityDAL,
|
||||||
|
orgDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const identityTokenAuthService = identityTokenAuthServiceFactory({
|
const identityTokenAuthService = identityTokenAuthServiceFactory({
|
||||||
|
identityDAL,
|
||||||
identityTokenAuthDAL,
|
identityTokenAuthDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
@@ -1641,6 +1652,7 @@ export const registerRoutes = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
const identityUaService = identityUaServiceFactory({
|
const identityUaService = identityUaServiceFactory({
|
||||||
|
identityDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
identityUaClientSecretDAL,
|
identityUaClientSecretDAL,
|
||||||
@@ -1652,6 +1664,7 @@ export const registerRoutes = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
const identityKubernetesAuthService = identityKubernetesAuthServiceFactory({
|
const identityKubernetesAuthService = identityKubernetesAuthServiceFactory({
|
||||||
|
identityDAL,
|
||||||
identityKubernetesAuthDAL,
|
identityKubernetesAuthDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
@@ -1665,6 +1678,7 @@ export const registerRoutes = async (
|
|||||||
membershipIdentityDAL
|
membershipIdentityDAL
|
||||||
});
|
});
|
||||||
const identityGcpAuthService = identityGcpAuthServiceFactory({
|
const identityGcpAuthService = identityGcpAuthServiceFactory({
|
||||||
|
identityDAL,
|
||||||
identityGcpAuthDAL,
|
identityGcpAuthDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
@@ -1674,6 +1688,7 @@ export const registerRoutes = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
const identityAliCloudAuthService = identityAliCloudAuthServiceFactory({
|
const identityAliCloudAuthService = identityAliCloudAuthServiceFactory({
|
||||||
|
identityDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
identityAliCloudAuthDAL,
|
identityAliCloudAuthDAL,
|
||||||
@@ -1683,6 +1698,7 @@ export const registerRoutes = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
const identityTlsCertAuthService = identityTlsCertAuthServiceFactory({
|
const identityTlsCertAuthService = identityTlsCertAuthServiceFactory({
|
||||||
|
identityDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
identityTlsCertAuthDAL,
|
identityTlsCertAuthDAL,
|
||||||
licenseService,
|
licenseService,
|
||||||
@@ -1692,6 +1708,7 @@ export const registerRoutes = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
const identityAwsAuthService = identityAwsAuthServiceFactory({
|
const identityAwsAuthService = identityAwsAuthServiceFactory({
|
||||||
|
identityDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
identityAwsAuthDAL,
|
identityAwsAuthDAL,
|
||||||
@@ -1701,6 +1718,7 @@ export const registerRoutes = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
const identityAzureAuthService = identityAzureAuthServiceFactory({
|
const identityAzureAuthService = identityAzureAuthServiceFactory({
|
||||||
|
identityDAL,
|
||||||
identityAzureAuthDAL,
|
identityAzureAuthDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
@@ -1710,6 +1728,7 @@ export const registerRoutes = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
const identityOciAuthService = identityOciAuthServiceFactory({
|
const identityOciAuthService = identityOciAuthServiceFactory({
|
||||||
|
identityDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
identityOciAuthDAL,
|
identityOciAuthDAL,
|
||||||
@@ -1733,6 +1752,7 @@ export const registerRoutes = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
const identityOidcAuthService = identityOidcAuthServiceFactory({
|
const identityOidcAuthService = identityOidcAuthServiceFactory({
|
||||||
|
identityDAL,
|
||||||
identityOidcAuthDAL,
|
identityOidcAuthDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
@@ -1743,6 +1763,7 @@ export const registerRoutes = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
const identityJwtAuthService = identityJwtAuthServiceFactory({
|
const identityJwtAuthService = identityJwtAuthServiceFactory({
|
||||||
|
identityDAL,
|
||||||
identityJwtAuthDAL,
|
identityJwtAuthDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
@@ -2238,7 +2259,13 @@ export const registerRoutes = async (
|
|||||||
pamSessionDAL,
|
pamSessionDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
userDAL
|
userDAL,
|
||||||
|
auditLogService
|
||||||
|
});
|
||||||
|
|
||||||
|
const pamAccountRotation = pamAccountRotationServiceFactory({
|
||||||
|
queueService,
|
||||||
|
pamAccountService
|
||||||
});
|
});
|
||||||
|
|
||||||
const pamSessionService = pamSessionServiceFactory({
|
const pamSessionService = pamSessionServiceFactory({
|
||||||
@@ -2272,16 +2299,38 @@ export const registerRoutes = async (
|
|||||||
// Start HSM service if it's configured/enabled.
|
// Start HSM service if it's configured/enabled.
|
||||||
await hsmService.startService();
|
await hsmService.startService();
|
||||||
|
|
||||||
|
const hsmStatus = await isHsmActiveAndEnabled({
|
||||||
|
hsmService,
|
||||||
|
kmsRootConfigDAL,
|
||||||
|
licenseService
|
||||||
|
});
|
||||||
|
|
||||||
|
// if the encryption strategy is software - user needs to provide an encryption key
|
||||||
|
// if the encryption strategy is null AND the hsm is not configured - user needs to provide an encryption key
|
||||||
|
const needsEncryptionKey =
|
||||||
|
hsmStatus.rootKmsConfigEncryptionStrategy === RootKeyEncryptionStrategy.Software ||
|
||||||
|
(hsmStatus.rootKmsConfigEncryptionStrategy === null && !hsmStatus.isHsmConfigured);
|
||||||
|
|
||||||
|
if (needsEncryptionKey) {
|
||||||
|
if (!envConfig.ROOT_ENCRYPTION_KEY && !envConfig.ENCRYPTION_KEY) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message:
|
||||||
|
"Root KMS encryption strategy is set to software. Please set the ENCRYPTION_KEY environment variable and restart your deployment.\nYou can enable HSM encryption in the Server Console."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await telemetryQueue.startTelemetryCheck();
|
await telemetryQueue.startTelemetryCheck();
|
||||||
await telemetryQueue.startAggregatedEventsJob();
|
await telemetryQueue.startAggregatedEventsJob();
|
||||||
await dailyResourceCleanUp.init();
|
await dailyResourceCleanUp.init();
|
||||||
await healthAlert.init();
|
await healthAlert.init();
|
||||||
await pkiSyncCleanup.init();
|
await pkiSyncCleanup.init();
|
||||||
|
await pamAccountRotation.init();
|
||||||
await dailyReminderQueueService.startDailyRemindersJob();
|
await dailyReminderQueueService.startDailyRemindersJob();
|
||||||
await dailyReminderQueueService.startSecretReminderMigrationJob();
|
await dailyReminderQueueService.startSecretReminderMigrationJob();
|
||||||
await dailyExpiringPkiItemAlert.startSendingAlerts();
|
await dailyExpiringPkiItemAlert.startSendingAlerts();
|
||||||
await pkiSubscriberQueue.startDailyAutoRenewalJob();
|
await pkiSubscriberQueue.startDailyAutoRenewalJob();
|
||||||
await kmsService.startService();
|
await kmsService.startService(hsmStatus);
|
||||||
await microsoftTeamsService.start();
|
await microsoftTeamsService.start();
|
||||||
await dynamicSecretQueueService.init();
|
await dynamicSecretQueueService.init();
|
||||||
await eventBusService.init();
|
await eventBusService.init();
|
||||||
@@ -2296,6 +2345,7 @@ export const registerRoutes = async (
|
|||||||
groupProject: groupProjectService,
|
groupProject: groupProjectService,
|
||||||
permission: permissionService,
|
permission: permissionService,
|
||||||
org: orgService,
|
org: orgService,
|
||||||
|
subOrganization: subOrgService,
|
||||||
oidc: oidcService,
|
oidc: oidcService,
|
||||||
apiKey: apiKeyService,
|
apiKey: apiKeyService,
|
||||||
authToken: tokenService,
|
authToken: tokenService,
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ export const registerAuthRoutes = async (server: FastifyZodProvider) => {
|
|||||||
decodedToken.userId,
|
decodedToken.userId,
|
||||||
decodedToken.organizationId,
|
decodedToken.organizationId,
|
||||||
decodedToken.authMethod,
|
decodedToken.authMethod,
|
||||||
|
decodedToken.organizationId,
|
||||||
decodedToken.organizationId
|
decodedToken.organizationId
|
||||||
);
|
);
|
||||||
if (org && org.userTokenExpiration) {
|
if (org && org.userTokenExpiration) {
|
||||||
|
|||||||
@@ -73,12 +73,12 @@ export const registerIdentityAliCloudAuthRouter = async (server: FastifyZodProvi
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const { identityAliCloudAuth, accessToken, identityAccessToken, identityMembershipOrg } =
|
const { identityAliCloudAuth, accessToken, identityAccessToken, identity } =
|
||||||
await server.services.identityAliCloudAuth.login(req.body);
|
await server.services.identityAliCloudAuth.login(req.body);
|
||||||
|
|
||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
orgId: identityMembershipOrg.scopeOrgId,
|
orgId: identity.orgId,
|
||||||
event: {
|
event: {
|
||||||
type: EventType.LOGIN_IDENTITY_ALICLOUD_AUTH,
|
type: EventType.LOGIN_IDENTITY_ALICLOUD_AUTH,
|
||||||
metadata: {
|
metadata: {
|
||||||
|
|||||||
@@ -40,12 +40,12 @@ export const registerIdentityAwsAuthRouter = async (server: FastifyZodProvider)
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const { identityAwsAuth, accessToken, identityAccessToken, identityMembershipOrg } =
|
const { identityAwsAuth, accessToken, identityAccessToken, identity } =
|
||||||
await server.services.identityAwsAuth.login(req.body);
|
await server.services.identityAwsAuth.login(req.body);
|
||||||
|
|
||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
orgId: identityMembershipOrg.scopeOrgId,
|
orgId: identity.orgId,
|
||||||
event: {
|
event: {
|
||||||
type: EventType.LOGIN_IDENTITY_AWS_AUTH,
|
type: EventType.LOGIN_IDENTITY_AWS_AUTH,
|
||||||
metadata: {
|
metadata: {
|
||||||
|
|||||||
@@ -35,12 +35,12 @@ export const registerIdentityAzureAuthRouter = async (server: FastifyZodProvider
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const { identityAzureAuth, accessToken, identityAccessToken, identityMembershipOrg } =
|
const { identityAzureAuth, accessToken, identityAccessToken, identity } =
|
||||||
await server.services.identityAzureAuth.login(req.body);
|
await server.services.identityAzureAuth.login(req.body);
|
||||||
|
|
||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
orgId: identityMembershipOrg.scopeOrgId,
|
orgId: identity.orgId,
|
||||||
event: {
|
event: {
|
||||||
type: EventType.LOGIN_IDENTITY_AZURE_AUTH,
|
type: EventType.LOGIN_IDENTITY_AZURE_AUTH,
|
||||||
metadata: {
|
metadata: {
|
||||||
|
|||||||
@@ -35,12 +35,12 @@ export const registerIdentityGcpAuthRouter = async (server: FastifyZodProvider)
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const { identityGcpAuth, accessToken, identityAccessToken, identityMembershipOrg } =
|
const { identityGcpAuth, accessToken, identityAccessToken, identity } =
|
||||||
await server.services.identityGcpAuth.login(req.body);
|
await server.services.identityGcpAuth.login(req.body);
|
||||||
|
|
||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
orgId: identityMembershipOrg.scopeOrgId,
|
orgId: identity.orgId,
|
||||||
event: {
|
event: {
|
||||||
type: EventType.LOGIN_IDENTITY_GCP_AUTH,
|
type: EventType.LOGIN_IDENTITY_GCP_AUTH,
|
||||||
metadata: {
|
metadata: {
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ export const registerIdentityJwtAuthRouter = async (server: FastifyZodProvider)
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const { identityJwtAuth, accessToken, identityAccessToken, identityMembershipOrg } =
|
const { identityJwtAuth, accessToken, identityAccessToken, identity } =
|
||||||
await server.services.identityJwtAuth.login({
|
await server.services.identityJwtAuth.login({
|
||||||
identityId: req.body.identityId,
|
identityId: req.body.identityId,
|
||||||
jwt: req.body.jwt
|
jwt: req.body.jwt
|
||||||
@@ -119,7 +119,7 @@ export const registerIdentityJwtAuthRouter = async (server: FastifyZodProvider)
|
|||||||
|
|
||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
orgId: identityMembershipOrg.scopeOrgId,
|
orgId: identity.orgId,
|
||||||
event: {
|
event: {
|
||||||
type: EventType.LOGIN_IDENTITY_JWT_AUTH,
|
type: EventType.LOGIN_IDENTITY_JWT_AUTH,
|
||||||
metadata: {
|
metadata: {
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export const registerIdentityKubernetesRouter = async (server: FastifyZodProvide
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const { identityKubernetesAuth, accessToken, identityAccessToken, identityMembershipOrg } =
|
const { identityKubernetesAuth, accessToken, identityAccessToken, identity } =
|
||||||
await server.services.identityKubernetesAuth.login({
|
await server.services.identityKubernetesAuth.login({
|
||||||
identityId: req.body.identityId,
|
identityId: req.body.identityId,
|
||||||
jwt: req.body.jwt
|
jwt: req.body.jwt
|
||||||
@@ -64,7 +64,7 @@ export const registerIdentityKubernetesRouter = async (server: FastifyZodProvide
|
|||||||
|
|
||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
orgId: identityMembershipOrg.scopeOrgId,
|
orgId: identity.orgId,
|
||||||
event: {
|
event: {
|
||||||
type: EventType.LOGIN_IDENTITY_KUBERNETES_AUTH,
|
type: EventType.LOGIN_IDENTITY_KUBERNETES_AUTH,
|
||||||
metadata: {
|
metadata: {
|
||||||
|
|||||||
@@ -162,13 +162,13 @@ export const registerIdentityLdapAuthRouter = async (server: FastifyZodProvider)
|
|||||||
|
|
||||||
const { identityId, user } = req.passportMachineIdentity;
|
const { identityId, user } = req.passportMachineIdentity;
|
||||||
|
|
||||||
const { accessToken, identityLdapAuth, identityMembershipOrg } = await server.services.identityLdapAuth.login({
|
const { accessToken, identityLdapAuth, identity } = await server.services.identityLdapAuth.login({
|
||||||
identityId
|
identityId
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
orgId: identityMembershipOrg.scopeOrgId,
|
orgId: identity.orgId,
|
||||||
event: {
|
event: {
|
||||||
type: EventType.LOGIN_IDENTITY_LDAP_AUTH,
|
type: EventType.LOGIN_IDENTITY_LDAP_AUTH,
|
||||||
metadata: {
|
metadata: {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user