mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Merge
This commit is contained in:
@@ -1,16 +1,16 @@
|
||||
import z from "zod";
|
||||
|
||||
import { readLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import {
|
||||
CreateOCIConnectionSchema,
|
||||
SanitizedOCIConnectionSchema,
|
||||
UpdateOCIConnectionSchema
|
||||
} from "@app/services/app-connection/oci";
|
||||
} from "@app/ee/services/app-connections/oci";
|
||||
import { readLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
import { registerAppConnectionEndpoints } from "./app-connection-endpoints";
|
||||
import { registerAppConnectionEndpoints } from "../../../../server/routes/v1/app-connection-routers/app-connection-endpoints";
|
||||
|
||||
export const registerOCIConnectionRouter = async (server: FastifyZodProvider) => {
|
||||
registerAppConnectionEndpoints({
|
||||
@@ -2,11 +2,10 @@ import {
|
||||
CreateOCIVaultSyncSchema,
|
||||
OCIVaultSyncSchema,
|
||||
UpdateOCIVaultSyncSchema
|
||||
} from "@app/services/secret-sync/oci-vault";
|
||||
} from "@app/ee/services/secret-sync/oci-vault";
|
||||
import { registerSyncSecretsEndpoints } from "@app/server/routes/v1/secret-sync-routers/secret-sync-endpoints";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
|
||||
import { registerSyncSecretsEndpoints } from "./secret-sync-endpoints";
|
||||
|
||||
export const registerOCIVaultSyncRouter = async (server: FastifyZodProvider) =>
|
||||
registerSyncSecretsEndpoints({
|
||||
destination: SecretSync.OCIVault,
|
||||
@@ -1,7 +1,9 @@
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { OrgServiceActor } from "@app/lib/types";
|
||||
|
||||
import { AppConnection } from "../app-connection-enums";
|
||||
import { AppConnection } from "../../../../services/app-connection/app-connection-enums";
|
||||
import { TLicenseServiceFactory } from "../../license/license-service";
|
||||
import { listOCICompartments, listOCIVaultKeys, listOCIVaults } from "./oci-connection-fns";
|
||||
import { TOCIConnection } from "./oci-connection-types";
|
||||
|
||||
@@ -22,8 +24,23 @@ type TListOCIVaultKeysDTO = {
|
||||
vaultOcid: string;
|
||||
};
|
||||
|
||||
export const ociConnectionService = (getAppConnection: TGetAppConnectionFunc) => {
|
||||
// Enterprise check
|
||||
export const checkPlan = async (licenseService: Pick<TLicenseServiceFactory, "getPlan">, orgId: string) => {
|
||||
const plan = await licenseService.getPlan(orgId);
|
||||
if (!plan.enterpriseAppConnections)
|
||||
throw new BadRequestError({
|
||||
message:
|
||||
"Failed to use app connection due to plan restriction. Upgrade plan to access enterprise app connections."
|
||||
});
|
||||
};
|
||||
|
||||
export const ociConnectionService = (
|
||||
getAppConnection: TGetAppConnectionFunc,
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">
|
||||
) => {
|
||||
const listCompartments = async (connectionId: string, actor: OrgServiceActor) => {
|
||||
await checkPlan(licenseService, actor.orgId);
|
||||
|
||||
const appConnection = await getAppConnection(AppConnection.OCI, connectionId, actor);
|
||||
|
||||
try {
|
||||
@@ -36,6 +53,8 @@ export const ociConnectionService = (getAppConnection: TGetAppConnectionFunc) =>
|
||||
};
|
||||
|
||||
const listVaults = async ({ connectionId, compartmentOcid }: TListOCIVaultsDTO, actor: OrgServiceActor) => {
|
||||
await checkPlan(licenseService, actor.orgId);
|
||||
|
||||
const appConnection = await getAppConnection(AppConnection.OCI, connectionId, actor);
|
||||
|
||||
try {
|
||||
@@ -51,6 +70,8 @@ export const ociConnectionService = (getAppConnection: TGetAppConnectionFunc) =>
|
||||
{ connectionId, compartmentOcid, vaultOcid }: TListOCIVaultKeysDTO,
|
||||
actor: OrgServiceActor
|
||||
) => {
|
||||
await checkPlan(licenseService, actor.orgId);
|
||||
|
||||
const appConnection = await getAppConnection(AppConnection.OCI, connectionId, actor);
|
||||
|
||||
try {
|
||||
@@ -2,7 +2,7 @@ import z from "zod";
|
||||
|
||||
import { DiscriminativePick } from "@app/lib/types";
|
||||
|
||||
import { AppConnection } from "../app-connection-enums";
|
||||
import { AppConnection } from "../../../../services/app-connection/app-connection-enums";
|
||||
import {
|
||||
CreateOCIConnectionSchema,
|
||||
OCIConnectionSchema,
|
||||
@@ -29,7 +29,9 @@ export const getDefaultOnPremFeatures = () => {
|
||||
secretApproval: true,
|
||||
secretRotation: true,
|
||||
caCrl: false,
|
||||
sshHostGroups: false
|
||||
sshHostGroups: false,
|
||||
enterpriseSecretSyncs: false,
|
||||
enterpriseAppConnections: false
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ export const licenseDALFactory = (db: TDbClient) => {
|
||||
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
||||
.where(`${TableName.Users}.isGhost`, false)
|
||||
.count();
|
||||
return Number(doc?.[0].count);
|
||||
return Number(doc?.[0]?.count ?? 0);
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Count of Org Members" });
|
||||
}
|
||||
|
||||
@@ -18,33 +18,33 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
|
||||
environmentsUsed: 0,
|
||||
identityLimit: null,
|
||||
identitiesUsed: 0,
|
||||
dynamicSecret: true,
|
||||
dynamicSecret: false,
|
||||
secretVersioning: true,
|
||||
pitRecovery: true,
|
||||
ipAllowlisting: true,
|
||||
rbac: true,
|
||||
githubOrgSync: true,
|
||||
pitRecovery: false,
|
||||
ipAllowlisting: false,
|
||||
rbac: false,
|
||||
githubOrgSync: false,
|
||||
customRateLimits: false,
|
||||
customAlerts: true,
|
||||
secretAccessInsights: true,
|
||||
auditLogs: true,
|
||||
auditLogsRetentionDays: 3,
|
||||
auditLogStreams: true,
|
||||
customAlerts: false,
|
||||
secretAccessInsights: false,
|
||||
auditLogs: false,
|
||||
auditLogsRetentionDays: 0,
|
||||
auditLogStreams: false,
|
||||
auditLogStreamLimit: 3,
|
||||
samlSSO: true,
|
||||
hsm: true,
|
||||
oidcSSO: true,
|
||||
scim: true,
|
||||
ldap: true,
|
||||
groups: true,
|
||||
samlSSO: false,
|
||||
hsm: false,
|
||||
oidcSSO: false,
|
||||
scim: false,
|
||||
ldap: false,
|
||||
groups: false,
|
||||
status: null,
|
||||
trial_end: null,
|
||||
has_used_trial: true,
|
||||
secretApproval: true,
|
||||
secretRotation: true,
|
||||
caCrl: true,
|
||||
instanceUserManagement: true,
|
||||
externalKms: true,
|
||||
secretApproval: false,
|
||||
secretRotation: false,
|
||||
caCrl: false,
|
||||
instanceUserManagement: false,
|
||||
externalKms: false,
|
||||
rateLimits: {
|
||||
readLimit: 60,
|
||||
writeLimit: 200,
|
||||
@@ -52,10 +52,12 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
|
||||
},
|
||||
pkiEst: false,
|
||||
enforceMfa: false,
|
||||
projectTemplates: true,
|
||||
kmip: true,
|
||||
gateway: true,
|
||||
sshHostGroups: true
|
||||
projectTemplates: false,
|
||||
kmip: false,
|
||||
gateway: false,
|
||||
sshHostGroups: false,
|
||||
enterpriseSecretSyncs: false,
|
||||
enterpriseAppConnections: false
|
||||
});
|
||||
|
||||
export const setupLicenseRequestWithStore = (baseURL: string, refreshUrl: string, licenseKey: string) => {
|
||||
|
||||
@@ -92,6 +92,10 @@ export const licenseServiceFactory = ({
|
||||
const {
|
||||
data: { currentPlan }
|
||||
} = await licenseServerOnPremApi.request.get<{ currentPlan: TFeatureSet }>("/api/license/v1/plan");
|
||||
|
||||
const workspacesUsed = await projectDAL.countOfOrgProjects(null);
|
||||
currentPlan.workspacesUsed = workspacesUsed;
|
||||
|
||||
onPremFeatures = currentPlan;
|
||||
logger.info("Successfully synchronized license key features");
|
||||
} catch (error) {
|
||||
@@ -185,6 +189,9 @@ export const licenseServiceFactory = ({
|
||||
} = await licenseServerCloudApi.request.get<{ currentPlan: TFeatureSet }>(
|
||||
`/api/license-server/v1/customers/${org.customerId}/cloud-plan`
|
||||
);
|
||||
const workspacesUsed = await projectDAL.countOfOrgProjects(orgId);
|
||||
currentPlan.workspacesUsed = workspacesUsed;
|
||||
|
||||
await keyStore.setItemWithExpiry(
|
||||
FEATURE_CACHE_KEY(org.id),
|
||||
LICENSE_SERVER_CLOUD_PLAN_TTL,
|
||||
|
||||
@@ -27,7 +27,7 @@ export type TFeatureSet = {
|
||||
slug: null;
|
||||
tier: -1;
|
||||
workspaceLimit: null;
|
||||
workspacesUsed: 0;
|
||||
workspacesUsed: number;
|
||||
dynamicSecret: false;
|
||||
memberLimit: null;
|
||||
membersUsed: number;
|
||||
@@ -72,6 +72,8 @@ export type TFeatureSet = {
|
||||
kmip: false;
|
||||
gateway: false;
|
||||
sshHostGroups: false;
|
||||
enterpriseSecretSyncs: false;
|
||||
enterpriseAppConnections: false;
|
||||
};
|
||||
|
||||
export type TOrgPlansTableDTO = {
|
||||
|
||||
@@ -6,5 +6,6 @@ export const OCI_VAULT_SYNC_LIST_OPTION: TSecretSyncListItem = {
|
||||
name: "OCI Vault",
|
||||
destination: SecretSync.OCIVault,
|
||||
connection: AppConnection.OCI,
|
||||
canImportSecrets: true
|
||||
canImportSecrets: true,
|
||||
enterprise: true
|
||||
};
|
||||
@@ -1,7 +1,6 @@
|
||||
import { secrets, vault } from "oci-sdk";
|
||||
|
||||
import { delay } from "@app/lib/delay";
|
||||
import { getOCIProvider } from "@app/services/app-connection/oci";
|
||||
import { getOCIProvider } from "@app/ee/services/app-connections/oci";
|
||||
import {
|
||||
TCreateOCIVaultVariable,
|
||||
TDeleteOCIVaultVariable,
|
||||
@@ -9,7 +8,8 @@ import {
|
||||
TOCIVaultSyncWithCredentials,
|
||||
TUnmarkOCIVaultVariableFromDeletion,
|
||||
TUpdateOCIVaultVariable
|
||||
} from "@app/services/secret-sync/oci-vault/oci-vault-sync-types";
|
||||
} from "@app/ee/services/secret-sync/oci-vault/oci-vault-sync-types";
|
||||
import { delay } from "@app/lib/delay";
|
||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
||||
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
|
||||
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||
@@ -66,5 +66,6 @@ export const OCIVaultSyncListItemSchema = z.object({
|
||||
name: z.literal("OCI Vault"),
|
||||
connection: z.literal(AppConnection.OCI),
|
||||
destination: z.literal(SecretSync.OCIVault),
|
||||
canImportSecrets: z.literal(true)
|
||||
canImportSecrets: z.literal(true),
|
||||
enterprise: z.boolean()
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import { SimpleAuthenticationDetailsProvider } from "oci-sdk";
|
||||
import { z } from "zod";
|
||||
|
||||
import { TOCIConnection } from "@app/services/app-connection/oci";
|
||||
import { TOCIConnection } from "@app/ee/services/app-connections/oci";
|
||||
|
||||
import { CreateOCIVaultSyncSchema, OCIVaultSyncListItemSchema, OCIVaultSyncSchema } from "./oci-vault-sync-schemas";
|
||||
|
||||
@@ -1014,7 +1014,8 @@ export const registerRoutes = async (
|
||||
secretVersionV2BridgeDAL,
|
||||
secretVersionTagV2BridgeDAL,
|
||||
resourceMetadataDAL,
|
||||
appConnectionDAL
|
||||
appConnectionDAL,
|
||||
licenseService
|
||||
});
|
||||
|
||||
const secretQueueService = secretQueueFactory({
|
||||
@@ -1631,7 +1632,8 @@ export const registerRoutes = async (
|
||||
const appConnectionService = appConnectionServiceFactory({
|
||||
appConnectionDAL,
|
||||
permissionService,
|
||||
kmsService
|
||||
kmsService,
|
||||
licenseService
|
||||
});
|
||||
|
||||
const secretSyncService = secretSyncServiceFactory({
|
||||
@@ -1642,7 +1644,8 @@ export const registerRoutes = async (
|
||||
folderDAL,
|
||||
secretSyncQueue,
|
||||
projectBotService,
|
||||
keyStore
|
||||
keyStore,
|
||||
licenseService
|
||||
});
|
||||
|
||||
const kmipService = kmipServiceFactory({
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { OCIConnectionListItemSchema, SanitizedOCIConnectionSchema } from "@app/ee/services/app-connections/oci";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { ApiDocsTags } from "@app/lib/api-docs";
|
||||
import { readLimit } from "@app/server/config/rateLimiter";
|
||||
@@ -39,7 +40,6 @@ import {
|
||||
import { LdapConnectionListItemSchema, SanitizedLdapConnectionSchema } from "@app/services/app-connection/ldap";
|
||||
import { MsSqlConnectionListItemSchema, SanitizedMsSqlConnectionSchema } from "@app/services/app-connection/mssql";
|
||||
import { MySqlConnectionListItemSchema, SanitizedMySqlConnectionSchema } from "@app/services/app-connection/mysql";
|
||||
import { OCIConnectionListItemSchema, SanitizedOCIConnectionSchema } from "@app/services/app-connection/oci";
|
||||
import {
|
||||
PostgresConnectionListItemSchema,
|
||||
SanitizedPostgresConnectionSchema
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { registerOCIConnectionRouter } from "@app/ee/routes/v1/app-connection-routers/oci-connection-router";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
|
||||
import { registerAuth0ConnectionRouter } from "./auth0-connection-router";
|
||||
@@ -14,7 +15,6 @@ import { registerHumanitecConnectionRouter } from "./humanitec-connection-router
|
||||
import { registerLdapConnectionRouter } from "./ldap-connection-router";
|
||||
import { registerMsSqlConnectionRouter } from "./mssql-connection-router";
|
||||
import { registerMySqlConnectionRouter } from "./mysql-connection-router";
|
||||
import { registerOCIConnectionRouter } from "./oci-connection-router";
|
||||
import { registerPostgresConnectionRouter } from "./postgres-connection-router";
|
||||
import { registerTeamCityConnectionRouter } from "./teamcity-connection-router";
|
||||
import { registerTerraformCloudConnectionRouter } from "./terraform-cloud-router";
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
|
||||
import { registerOCIVaultSyncRouter } from "../../../../ee/routes/v1/secret-sync-routers/oci-vault-sync-router";
|
||||
import { registerAwsParameterStoreSyncRouter } from "./aws-parameter-store-sync-router";
|
||||
import { registerAwsSecretsManagerSyncRouter } from "./aws-secrets-manager-sync-router";
|
||||
import { registerAzureAppConfigurationSyncRouter } from "./azure-app-configuration-sync-router";
|
||||
@@ -10,7 +11,6 @@ import { registerGcpSyncRouter } from "./gcp-sync-router";
|
||||
import { registerGitHubSyncRouter } from "./github-sync-router";
|
||||
import { registerHCVaultSyncRouter } from "./hc-vault-sync-router";
|
||||
import { registerHumanitecSyncRouter } from "./humanitec-sync-router";
|
||||
import { registerOCIVaultSyncRouter } from "./oci-vault-sync-router";
|
||||
import { registerTeamCitySyncRouter } from "./teamcity-sync-router";
|
||||
import { registerTerraformCloudSyncRouter } from "./terraform-cloud-sync-router";
|
||||
import { registerVercelSyncRouter } from "./vercel-sync-router";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { OCIVaultSyncListItemSchema, OCIVaultSyncSchema } from "@app/ee/services/secret-sync/oci-vault";
|
||||
import { ApiDocsTags, SecretSyncs } from "@app/lib/api-docs";
|
||||
import { readLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
@@ -24,7 +25,6 @@ import { GcpSyncListItemSchema, GcpSyncSchema } from "@app/services/secret-sync/
|
||||
import { GitHubSyncListItemSchema, GitHubSyncSchema } from "@app/services/secret-sync/github";
|
||||
import { HCVaultSyncListItemSchema, HCVaultSyncSchema } from "@app/services/secret-sync/hc-vault";
|
||||
import { HumanitecSyncListItemSchema, HumanitecSyncSchema } from "@app/services/secret-sync/humanitec";
|
||||
import { OCIVaultSyncListItemSchema, OCIVaultSyncSchema } from "@app/services/secret-sync/oci-vault";
|
||||
import { TeamCitySyncListItemSchema, TeamCitySyncSchema } from "@app/services/secret-sync/teamcity";
|
||||
import { TerraformCloudSyncListItemSchema, TerraformCloudSyncSchema } from "@app/services/secret-sync/terraform-cloud";
|
||||
import { VercelSyncListItemSchema, VercelSyncSchema } from "@app/services/secret-sync/vercel";
|
||||
|
||||
@@ -67,3 +67,8 @@ export enum AWSRegion {
|
||||
// South America
|
||||
SA_EAST_1 = "sa-east-1" // Sao Paulo
|
||||
}
|
||||
|
||||
export enum AppConnectionPlanType {
|
||||
Enterprise = "enterprise",
|
||||
Regular = "regular"
|
||||
}
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
import { TAppConnections } from "@app/db/schemas/app-connections";
|
||||
import {
|
||||
getOCIConnectionListItem,
|
||||
OCIConnectionMethod,
|
||||
validateOCIConnectionCredentials
|
||||
} from "@app/ee/services/app-connections/oci";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { generateHash } from "@app/lib/crypto/encryption";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { APP_CONNECTION_NAME_MAP } from "@app/services/app-connection/app-connection-maps";
|
||||
import { APP_CONNECTION_NAME_MAP, APP_CONNECTION_PLAN_MAP } from "@app/services/app-connection/app-connection-maps";
|
||||
import {
|
||||
transferSqlConnectionCredentialsToPlatform,
|
||||
validateSqlConnectionCredentials
|
||||
} from "@app/services/app-connection/shared/sql";
|
||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||
|
||||
import { AppConnection } from "./app-connection-enums";
|
||||
import { AppConnection, AppConnectionPlanType } from "./app-connection-enums";
|
||||
import { TAppConnectionServiceFactoryDep } from "./app-connection-service";
|
||||
import {
|
||||
TAppConnection,
|
||||
@@ -55,7 +61,6 @@ import { getLdapConnectionListItem, LdapConnectionMethod, validateLdapConnection
|
||||
import { getMsSqlConnectionListItem, MsSqlConnectionMethod } from "./mssql";
|
||||
import { MySqlConnectionMethod } from "./mysql/mysql-connection-enums";
|
||||
import { getMySqlConnectionListItem } from "./mysql/mysql-connection-fns";
|
||||
import { getOCIConnectionListItem, OCIConnectionMethod, validateOCIConnectionCredentials } from "./oci";
|
||||
import { getPostgresConnectionListItem, PostgresConnectionMethod } from "./postgres";
|
||||
import {
|
||||
getTeamCityConnectionListItem,
|
||||
@@ -263,3 +268,18 @@ export const TRANSITION_CONNECTION_CREDENTIALS_TO_PLATFORM: Record<
|
||||
[AppConnection.TeamCity]: platformManagedCredentialsNotSupported,
|
||||
[AppConnection.OCI]: platformManagedCredentialsNotSupported
|
||||
};
|
||||
|
||||
export const enterpriseAppCheck = async (
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">,
|
||||
appConnection: AppConnection,
|
||||
orgId: string,
|
||||
errorMessage: string
|
||||
) => {
|
||||
if (APP_CONNECTION_PLAN_MAP[appConnection] === AppConnectionPlanType.Enterprise) {
|
||||
const plan = await licenseService.getPlan(orgId);
|
||||
if (!plan.enterpriseAppConnections)
|
||||
throw new BadRequestError({
|
||||
message: errorMessage
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AppConnection } from "./app-connection-enums";
|
||||
import { AppConnection, AppConnectionPlanType } from "./app-connection-enums";
|
||||
|
||||
export const APP_CONNECTION_NAME_MAP: Record<AppConnection, string> = {
|
||||
[AppConnection.AWS]: "AWS",
|
||||
@@ -22,3 +22,26 @@ export const APP_CONNECTION_NAME_MAP: Record<AppConnection, string> = {
|
||||
[AppConnection.TeamCity]: "TeamCity",
|
||||
[AppConnection.OCI]: "OCI"
|
||||
};
|
||||
|
||||
export const APP_CONNECTION_PLAN_MAP: Record<AppConnection, AppConnectionPlanType> = {
|
||||
[AppConnection.AWS]: AppConnectionPlanType.Regular,
|
||||
[AppConnection.GitHub]: AppConnectionPlanType.Regular,
|
||||
[AppConnection.GCP]: AppConnectionPlanType.Regular,
|
||||
[AppConnection.AzureKeyVault]: AppConnectionPlanType.Regular,
|
||||
[AppConnection.AzureAppConfiguration]: AppConnectionPlanType.Regular,
|
||||
[AppConnection.AzureClientSecrets]: AppConnectionPlanType.Regular,
|
||||
[AppConnection.Databricks]: AppConnectionPlanType.Regular,
|
||||
[AppConnection.Humanitec]: AppConnectionPlanType.Regular,
|
||||
[AppConnection.TerraformCloud]: AppConnectionPlanType.Regular,
|
||||
[AppConnection.Vercel]: AppConnectionPlanType.Regular,
|
||||
[AppConnection.Postgres]: AppConnectionPlanType.Regular,
|
||||
[AppConnection.MsSql]: AppConnectionPlanType.Regular,
|
||||
[AppConnection.Camunda]: AppConnectionPlanType.Regular,
|
||||
[AppConnection.Windmill]: AppConnectionPlanType.Regular,
|
||||
[AppConnection.Auth0]: AppConnectionPlanType.Regular,
|
||||
[AppConnection.HCVault]: AppConnectionPlanType.Regular,
|
||||
[AppConnection.LDAP]: AppConnectionPlanType.Regular,
|
||||
[AppConnection.TeamCity]: AppConnectionPlanType.Regular,
|
||||
[AppConnection.OCI]: AppConnectionPlanType.Enterprise,
|
||||
[AppConnection.MySql]: AppConnectionPlanType.Regular
|
||||
};
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
|
||||
import { ValidateOCIConnectionCredentialsSchema } from "@app/ee/services/app-connections/oci";
|
||||
import { ociConnectionService } from "@app/ee/services/app-connections/oci/oci-connection-service";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { OrgPermissionAppConnectionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { generateHash } from "@app/lib/crypto/encryption";
|
||||
@@ -9,6 +12,7 @@ import { DiscriminativePick, OrgServiceActor } from "@app/lib/types";
|
||||
import {
|
||||
decryptAppConnection,
|
||||
encryptAppConnectionCredentials,
|
||||
enterpriseAppCheck,
|
||||
getAppConnectionMethodName,
|
||||
listAppConnectionOptions,
|
||||
TRANSITION_CONNECTION_CREDENTIALS_TO_PLATFORM,
|
||||
@@ -50,8 +54,6 @@ import { humanitecConnectionService } from "./humanitec/humanitec-connection-ser
|
||||
import { ValidateLdapConnectionCredentialsSchema } from "./ldap";
|
||||
import { ValidateMsSqlConnectionCredentialsSchema } from "./mssql";
|
||||
import { ValidateMySqlConnectionCredentialsSchema } from "./mysql";
|
||||
import { ValidateOCIConnectionCredentialsSchema } from "./oci";
|
||||
import { ociConnectionService } from "./oci/oci-connection-service";
|
||||
import { ValidatePostgresConnectionCredentialsSchema } from "./postgres";
|
||||
import { ValidateTeamCityConnectionCredentialsSchema } from "./teamcity";
|
||||
import { teamcityConnectionService } from "./teamcity/teamcity-connection-service";
|
||||
@@ -66,6 +68,7 @@ export type TAppConnectionServiceFactoryDep = {
|
||||
appConnectionDAL: TAppConnectionDALFactory;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
};
|
||||
|
||||
export type TAppConnectionServiceFactory = ReturnType<typeof appConnectionServiceFactory>;
|
||||
@@ -96,7 +99,8 @@ const VALIDATE_APP_CONNECTION_CREDENTIALS_MAP: Record<AppConnection, TValidateAp
|
||||
export const appConnectionServiceFactory = ({
|
||||
appConnectionDAL,
|
||||
permissionService,
|
||||
kmsService
|
||||
kmsService,
|
||||
licenseService
|
||||
}: TAppConnectionServiceFactoryDep) => {
|
||||
const listAppConnectionsByOrg = async (actor: OrgServiceActor, app?: AppConnection) => {
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
@@ -193,6 +197,13 @@ export const appConnectionServiceFactory = ({
|
||||
OrgPermissionSubjects.AppConnections
|
||||
);
|
||||
|
||||
await enterpriseAppCheck(
|
||||
licenseService,
|
||||
app,
|
||||
actor.orgId,
|
||||
"Failed to create app connection due to plan restriction. Upgrade plan to access enterprise app connections."
|
||||
);
|
||||
|
||||
const validatedCredentials = await validateAppConnectionCredentials({
|
||||
app,
|
||||
credentials,
|
||||
@@ -255,6 +266,13 @@ export const appConnectionServiceFactory = ({
|
||||
|
||||
if (!appConnection) throw new NotFoundError({ message: `Could not find App Connection with ID ${connectionId}` });
|
||||
|
||||
await enterpriseAppCheck(
|
||||
licenseService,
|
||||
appConnection.app as AppConnection,
|
||||
actor.orgId,
|
||||
"Failed to update app connection due to plan restriction. Upgrade plan to access enterprise app connections."
|
||||
);
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
actor.type,
|
||||
actor.id,
|
||||
@@ -401,6 +419,13 @@ export const appConnectionServiceFactory = ({
|
||||
|
||||
if (!appConnection) throw new NotFoundError({ message: `Could not find App Connection with ID ${connectionId}` });
|
||||
|
||||
await enterpriseAppCheck(
|
||||
licenseService,
|
||||
app,
|
||||
actor.orgId,
|
||||
"Failed to connect app due to plan restriction. Upgrade plan to access enterprise app connections."
|
||||
);
|
||||
|
||||
const { permission: orgPermission } = await permissionService.getOrgPermission(
|
||||
actor.type,
|
||||
actor.id,
|
||||
@@ -470,6 +495,6 @@ export const appConnectionServiceFactory = ({
|
||||
hcvault: hcVaultConnectionService(connectAppConnectionById),
|
||||
windmill: windmillConnectionService(connectAppConnectionById),
|
||||
teamcity: teamcityConnectionService(connectAppConnectionById),
|
||||
oci: ociConnectionService(connectAppConnectionById)
|
||||
oci: ociConnectionService(connectAppConnectionById, licenseService)
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2,6 +2,12 @@ import { TAppConnectionDALFactory } from "@app/services/app-connection/app-conne
|
||||
import { TSqlConnectionConfig } from "@app/services/app-connection/shared/sql/sql-connection-types";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
|
||||
import {
|
||||
TOCIConnection,
|
||||
TOCIConnectionConfig,
|
||||
TOCIConnectionInput,
|
||||
TValidateOCIConnectionCredentialsSchema
|
||||
} from "../../ee/services/app-connections/oci";
|
||||
import { AWSRegion } from "./app-connection-enums";
|
||||
import {
|
||||
TAuth0Connection,
|
||||
@@ -77,12 +83,6 @@ import {
|
||||
} from "./ldap";
|
||||
import { TMsSqlConnection, TMsSqlConnectionInput, TValidateMsSqlConnectionCredentialsSchema } from "./mssql";
|
||||
import { TMySqlConnection, TMySqlConnectionInput, TValidateMySqlConnectionCredentialsSchema } from "./mysql";
|
||||
import {
|
||||
TOCIConnection,
|
||||
TOCIConnectionConfig,
|
||||
TOCIConnectionInput,
|
||||
TValidateOCIConnectionCredentialsSchema
|
||||
} from "./oci";
|
||||
import {
|
||||
TPostgresConnection,
|
||||
TPostgresConnectionInput,
|
||||
|
||||
@@ -425,6 +425,21 @@ export const projectDALFactory = (db: TDbClient) => {
|
||||
return { docs, totalCount: Number(docs?.[0]?.count ?? 0) };
|
||||
};
|
||||
|
||||
const countOfOrgProjects = async (orgId: string | null, tx?: Knex) => {
|
||||
try {
|
||||
const doc = await (tx || db.replicaNode())(TableName.Project)
|
||||
.andWhere((bd) => {
|
||||
if (orgId) {
|
||||
void bd.where({ orgId });
|
||||
}
|
||||
})
|
||||
.count();
|
||||
return Number(doc?.[0]?.count ?? 0);
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Count of Org Projects" });
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
...projectOrm,
|
||||
findUserProjects,
|
||||
@@ -437,6 +452,7 @@ export const projectDALFactory = (db: TDbClient) => {
|
||||
findProjectWithOrg,
|
||||
checkProjectUpgradeStatus,
|
||||
getProjectFromSplitId,
|
||||
searchProjects
|
||||
searchProjects,
|
||||
countOfOrgProjects
|
||||
};
|
||||
};
|
||||
|
||||
@@ -26,3 +26,8 @@ export enum SecretSyncImportBehavior {
|
||||
PrioritizeSource = "prioritize-source",
|
||||
PrioritizeDestination = "prioritize-destination"
|
||||
}
|
||||
|
||||
export enum SecretSyncPlanType {
|
||||
Enterprise = "enterprise",
|
||||
Regular = "regular"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { AxiosError } from "axios";
|
||||
import RE2 from "re2";
|
||||
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { OCI_VAULT_SYNC_LIST_OPTION, OCIVaultSyncFns } from "@app/ee/services/secret-sync/oci-vault";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import {
|
||||
AWS_PARAMETER_STORE_SYNC_LIST_OPTION,
|
||||
AwsParameterStoreSyncFns
|
||||
@@ -11,7 +14,7 @@ import {
|
||||
} from "@app/services/secret-sync/aws-secrets-manager";
|
||||
import { DATABRICKS_SYNC_LIST_OPTION, databricksSyncFactory } from "@app/services/secret-sync/databricks";
|
||||
import { GITHUB_SYNC_LIST_OPTION, GithubSyncFns } from "@app/services/secret-sync/github";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
import { SecretSync, SecretSyncPlanType } from "@app/services/secret-sync/secret-sync-enums";
|
||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
||||
import {
|
||||
TSecretMap,
|
||||
@@ -29,7 +32,7 @@ import { GcpSyncFns } from "./gcp/gcp-sync-fns";
|
||||
import { HC_VAULT_SYNC_LIST_OPTION, HCVaultSyncFns } from "./hc-vault";
|
||||
import { HUMANITEC_SYNC_LIST_OPTION } from "./humanitec";
|
||||
import { HumanitecSyncFns } from "./humanitec/humanitec-sync-fns";
|
||||
import { OCI_VAULT_SYNC_LIST_OPTION, OCIVaultSyncFns } from "./oci-vault";
|
||||
import { SECRET_SYNC_PLAN_MAP } from "./secret-sync-maps";
|
||||
import { TEAMCITY_SYNC_LIST_OPTION, TeamCitySyncFns } from "./teamcity";
|
||||
import { TERRAFORM_CLOUD_SYNC_LIST_OPTION, TerraformCloudSyncFns } from "./terraform-cloud";
|
||||
import { VERCEL_SYNC_LIST_OPTION, VercelSyncFns } from "./vercel";
|
||||
@@ -327,3 +330,18 @@ export const parseSyncErrorMessage = (err: unknown): string => {
|
||||
? errorMessage
|
||||
: `${errorMessage.substring(0, MAX_MESSAGE_LENGTH - 3)}...`;
|
||||
};
|
||||
|
||||
export const enterpriseSyncCheck = async (
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">,
|
||||
secretSync: SecretSync,
|
||||
orgId: string,
|
||||
errorMessage: string
|
||||
) => {
|
||||
if (SECRET_SYNC_PLAN_MAP[secretSync] === SecretSyncPlanType.Enterprise) {
|
||||
const plan = await licenseService.getPlan(orgId);
|
||||
if (!plan.enterpriseSecretSyncs)
|
||||
throw new BadRequestError({
|
||||
message: errorMessage
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
import { SecretSync, SecretSyncPlanType } from "@app/services/secret-sync/secret-sync-enums";
|
||||
|
||||
export const SECRET_SYNC_NAME_MAP: Record<SecretSync, string> = {
|
||||
[SecretSync.AWSParameterStore]: "AWS Parameter Store",
|
||||
@@ -36,3 +36,21 @@ export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
|
||||
[SecretSync.TeamCity]: AppConnection.TeamCity,
|
||||
[SecretSync.OCIVault]: AppConnection.OCI
|
||||
};
|
||||
|
||||
export const SECRET_SYNC_PLAN_MAP: Record<SecretSync, SecretSyncPlanType> = {
|
||||
[SecretSync.AWSParameterStore]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.AWSSecretsManager]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.GitHub]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.GCPSecretManager]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.AzureKeyVault]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.AzureAppConfiguration]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.Databricks]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.Humanitec]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.TerraformCloud]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.Camunda]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.Vercel]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.Windmill]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.HCVault]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.TeamCity]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.OCIVault]: SecretSyncPlanType.Enterprise
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Job } from "bullmq";
|
||||
import { ProjectMembershipRole, SecretType } from "@app/db/schemas";
|
||||
import { TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-service";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { KeyStorePrefixes, TKeyStoreFactory } from "@app/keystore/keystore";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { logger } from "@app/lib/logger";
|
||||
@@ -32,7 +33,7 @@ import {
|
||||
SecretSyncInitialSyncBehavior
|
||||
} from "@app/services/secret-sync/secret-sync-enums";
|
||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
||||
import { parseSyncErrorMessage, SecretSyncFns } from "@app/services/secret-sync/secret-sync-fns";
|
||||
import { enterpriseSyncCheck, parseSyncErrorMessage, SecretSyncFns } from "@app/services/secret-sync/secret-sync-fns";
|
||||
import { SECRET_SYNC_NAME_MAP } from "@app/services/secret-sync/secret-sync-maps";
|
||||
import {
|
||||
SecretSyncAction,
|
||||
@@ -93,6 +94,7 @@ type TSecretSyncQueueFactoryDep = {
|
||||
secretVersionV2BridgeDAL: Pick<TSecretVersionV2DALFactory, "insertMany" | "findLatestVersionMany">;
|
||||
secretVersionTagV2BridgeDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany">;
|
||||
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
};
|
||||
|
||||
type SecretSyncActionJob = Job<
|
||||
@@ -133,7 +135,8 @@ export const secretSyncQueueFactory = ({
|
||||
secretVersionTagDAL,
|
||||
secretVersionV2BridgeDAL,
|
||||
secretVersionTagV2BridgeDAL,
|
||||
resourceMetadataDAL
|
||||
resourceMetadataDAL,
|
||||
licenseService
|
||||
}: TSecretSyncQueueFactoryDep) => {
|
||||
const appCfg = getConfig();
|
||||
|
||||
@@ -323,7 +326,20 @@ export const secretSyncQueueFactory = ({
|
||||
secretSync: TSecretSyncWithCredentials,
|
||||
importBehavior: SecretSyncImportBehavior
|
||||
): Promise<TSecretMap> => {
|
||||
const { projectId, environment, folder } = secretSync;
|
||||
const {
|
||||
projectId,
|
||||
environment,
|
||||
folder,
|
||||
destination,
|
||||
connection: { orgId }
|
||||
} = secretSync;
|
||||
|
||||
await enterpriseSyncCheck(
|
||||
licenseService,
|
||||
destination,
|
||||
orgId,
|
||||
"Failed to import secrets due to plan restriction. Upgrade plan to access enterprise secret syncs."
|
||||
);
|
||||
|
||||
if (!environment || !folder)
|
||||
throw new Error(
|
||||
@@ -400,6 +416,13 @@ export const secretSyncQueueFactory = ({
|
||||
|
||||
if (!secretSync) throw new Error(`Cannot find secret sync with ID ${syncId}`);
|
||||
|
||||
await enterpriseSyncCheck(
|
||||
licenseService,
|
||||
secretSync.destination as SecretSync,
|
||||
secretSync.connection.orgId,
|
||||
"Failed to sync secrets due to plan restriction. Upgrade plan to access enterprise secret syncs."
|
||||
);
|
||||
|
||||
await secretSyncDAL.updateById(syncId, {
|
||||
syncStatus: SecretSyncStatus.Running
|
||||
});
|
||||
@@ -659,6 +682,13 @@ export const secretSyncQueueFactory = ({
|
||||
|
||||
if (!secretSync) throw new Error(`Cannot find secret sync with ID ${syncId}`);
|
||||
|
||||
await enterpriseSyncCheck(
|
||||
licenseService,
|
||||
secretSync.destination as SecretSync,
|
||||
secretSync.connection.orgId,
|
||||
"Failed to remove secrets due to plan restriction. Upgrade plan to access enterprise secret syncs."
|
||||
);
|
||||
|
||||
await secretSyncDAL.updateById(syncId, {
|
||||
removeStatus: SecretSyncStatus.Running
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { ActionProjectType } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { throwIfMissingSecretReadValueOrDescribePermission } from "@app/ee/services/permission/permission-fns";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import {
|
||||
@@ -16,7 +17,7 @@ import { TAppConnectionServiceFactory } from "@app/services/app-connection/app-c
|
||||
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
|
||||
import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
import { listSecretSyncOptions } from "@app/services/secret-sync/secret-sync-fns";
|
||||
import { enterpriseSyncCheck, listSecretSyncOptions } from "@app/services/secret-sync/secret-sync-fns";
|
||||
import {
|
||||
SecretSyncStatus,
|
||||
TCreateSecretSyncDTO,
|
||||
@@ -49,6 +50,7 @@ type TSecretSyncServiceFactoryDep = {
|
||||
TSecretSyncQueueFactory,
|
||||
"queueSecretSyncSyncSecretsById" | "queueSecretSyncImportSecretsById" | "queueSecretSyncRemoveSecretsById"
|
||||
>;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
};
|
||||
|
||||
export type TSecretSyncServiceFactory = ReturnType<typeof secretSyncServiceFactory>;
|
||||
@@ -61,7 +63,8 @@ export const secretSyncServiceFactory = ({
|
||||
appConnectionService,
|
||||
projectBotService,
|
||||
secretSyncQueue,
|
||||
keyStore
|
||||
keyStore,
|
||||
licenseService
|
||||
}: TSecretSyncServiceFactoryDep) => {
|
||||
const listSecretSyncsByProjectId = async (
|
||||
{ projectId, destination }: TListSecretSyncsByProjectId,
|
||||
@@ -191,6 +194,13 @@ export const secretSyncServiceFactory = ({
|
||||
{ projectId, secretPath, environment, ...params }: TCreateSecretSyncDTO,
|
||||
actor: OrgServiceActor
|
||||
) => {
|
||||
await enterpriseSyncCheck(
|
||||
licenseService,
|
||||
params.destination,
|
||||
actor.orgId,
|
||||
"Failed to create secret sync due to plan restriction. Upgrade plan to access enterprise secret syncs."
|
||||
);
|
||||
|
||||
const { permission: projectPermission } = await permissionService.getProjectPermission({
|
||||
actor: actor.type,
|
||||
actorId: actor.id,
|
||||
@@ -260,6 +270,13 @@ export const secretSyncServiceFactory = ({
|
||||
message: `Could not find ${SECRET_SYNC_NAME_MAP[destination]} Sync with ID ${syncId}`
|
||||
});
|
||||
|
||||
await enterpriseSyncCheck(
|
||||
licenseService,
|
||||
secretSync.destination as SecretSync,
|
||||
actor.orgId,
|
||||
"Failed to update secret sync due to plan restriction. Upgrade plan to access enterprise secret syncs."
|
||||
);
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor: actor.type,
|
||||
actorId: actor.id,
|
||||
@@ -408,6 +425,13 @@ export const secretSyncServiceFactory = ({
|
||||
message: `Could not find ${SECRET_SYNC_NAME_MAP[destination]} Sync with ID "${syncId}"`
|
||||
});
|
||||
|
||||
await enterpriseSyncCheck(
|
||||
licenseService,
|
||||
secretSync.destination as SecretSync,
|
||||
actor.orgId,
|
||||
"Failed to trigger secret sync due to plan restriction. Upgrade plan to access enterprise secret syncs."
|
||||
);
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor: actor.type,
|
||||
actorId: actor.id,
|
||||
@@ -463,6 +487,13 @@ export const secretSyncServiceFactory = ({
|
||||
message: `Could not find ${SECRET_SYNC_NAME_MAP[destination]} Sync with ID "${syncId}"`
|
||||
});
|
||||
|
||||
await enterpriseSyncCheck(
|
||||
licenseService,
|
||||
secretSync.destination as SecretSync,
|
||||
actor.orgId,
|
||||
"Failed to trigger secret sync due to plan restriction. Upgrade plan to access enterprise secret syncs."
|
||||
);
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor: actor.type,
|
||||
actorId: actor.id,
|
||||
@@ -512,6 +543,13 @@ export const secretSyncServiceFactory = ({
|
||||
message: `Could not find ${SECRET_SYNC_NAME_MAP[destination]} Sync with ID "${syncId}"`
|
||||
});
|
||||
|
||||
await enterpriseSyncCheck(
|
||||
licenseService,
|
||||
secretSync.destination as SecretSync,
|
||||
actor.orgId,
|
||||
"Failed to trigger secret sync due to plan restriction. Upgrade plan to access enterprise secret syncs."
|
||||
);
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor: actor.type,
|
||||
actorId: actor.id,
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { Job } from "bullmq";
|
||||
|
||||
import { AuditLogInfo } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import {
|
||||
TOCIVaultSync,
|
||||
TOCIVaultSyncInput,
|
||||
TOCIVaultSyncListItem,
|
||||
TOCIVaultSyncWithCredentials
|
||||
} from "@app/ee/services/secret-sync/oci-vault";
|
||||
import { QueueJobs } from "@app/queue";
|
||||
import { ResourceMetadataDTO } from "@app/services/resource-metadata/resource-metadata-schema";
|
||||
import {
|
||||
@@ -67,7 +73,6 @@ import {
|
||||
THumanitecSyncListItem,
|
||||
THumanitecSyncWithCredentials
|
||||
} from "./humanitec";
|
||||
import { TOCIVaultSync, TOCIVaultSyncInput, TOCIVaultSyncListItem, TOCIVaultSyncWithCredentials } from "./oci-vault";
|
||||
import {
|
||||
TTeamCitySync,
|
||||
TTeamCitySyncInput,
|
||||
|
||||
@@ -5,6 +5,7 @@ import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/pe
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
|
||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
||||
import { TokenType } from "@app/services/auth-token/auth-token-types";
|
||||
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
||||
@@ -80,6 +81,17 @@ export const userServiceFactory = ({
|
||||
const verifyEmailVerificationCode = async (username: string, code: string) => {
|
||||
// akhilmhdh: case sensitive email resolution
|
||||
const usersByusername = await userDAL.findUserByUsername(username);
|
||||
|
||||
logger.info(
|
||||
usersByusername.map((user) => ({
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
username: user.username,
|
||||
isEmailVerified: user.isEmailVerified
|
||||
})),
|
||||
`Verify email users: [username=${username}]`
|
||||
);
|
||||
|
||||
const user =
|
||||
usersByusername?.length > 1 ? usersByusername.find((el) => el.username === username) : usersByusername?.[0];
|
||||
if (!user) throw new NotFoundError({ name: `User with username '${username}' not found` });
|
||||
|
||||
@@ -14,7 +14,7 @@ description: "Learn how to configure Auth0 OIDC for Infisical SSO."
|
||||
<Step title="Setup application in Auth0">
|
||||
1.1. From the Application's Page, navigate to the settings tab of the Auth0 application you want to integrate with Infisical.
|
||||

|
||||
|
||||
|
||||
1.2. In the Application URIs section, set the **Application Login URI** and **Allowed Web Origins** fields to `https://app.infisical.com` and the **Allowed Callback URL** field to `https://app.infisical.com/api/v1/sso/oidc/callback`.
|
||||

|
||||

|
||||
@@ -70,7 +70,7 @@ description: "Learn how to configure Auth0 OIDC for Infisical SSO."
|
||||
prior to enforcing OIDC SSO to prevent any unintended issues.
|
||||
</Warning>
|
||||
<Info>
|
||||
In case of a lockout, an organization admin can use the admin login portal in the `/login/admin` path e.g. https://app.infisical.com/login/admin.
|
||||
In case of a lockout, an organization admin can use the [Admin Login Portal](https://infisical.com/docs/documentation/platform/sso/overview#admin-login-portal) in the `/login/admin` path e.g. https://app.infisical.com/login/admin.
|
||||
</Info>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
@@ -23,30 +23,30 @@ description: "Learn how to configure Auth0 SAML for Infisical SSO."
|
||||
</Step>
|
||||
<Step title="Create a SAML application in Auth0">
|
||||
2.1. In your Auth0 account, head to Applications and create an application.
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
Select **Regular Web Application** and press **Create**.
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
2.2. In the Application head to Settings > Application URIs and add the **Application Callback URL** from step 1 into the **Allowed Callback URLs** field.
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
2.3. In the Application head to Addons > SAML2 Web App and copy the **Issuer**, **Identity Provider Login URL**, and **Identity Provider Certificate** from the **Usage** tab.
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
2.4. Back in Infisical, set **Issuer**, **Identity Provider Login URL**, and **Certificate** to the corresponding items from step 2.3.
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
2.5. Back in Auth0, in the **Settings** tab, set the **Application Callback URL** to the **Application Callback URL** from step 1
|
||||
and update the **Settings** field with the JSON under the picture below (replacing `<audience-from-infisical>` with the **Audience** from step 1).
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"audience": "<audience-from-infisical>",
|
||||
@@ -76,7 +76,7 @@ description: "Learn how to configure Auth0 SAML for Infisical SSO."
|
||||
Once you've completed this requirement, you can toggle the **Enforce SAML SSO** button to enforce SAML SSO.
|
||||
|
||||
<Info>
|
||||
In case of a lockout, an organization admin can use the admin login portal in the `/login/admin` path e.g. https://app.infisical.com/login/admin.
|
||||
In case of a lockout, an organization admin can use the [Admin Login Portal](https://infisical.com/docs/documentation/platform/sso/overview#admin-login-portal) in the `/login/admin` path e.g. https://app.infisical.com/login/admin.
|
||||
</Info>
|
||||
</Step>
|
||||
|
||||
@@ -96,4 +96,4 @@ description: "Learn how to configure Auth0 SAML for Infisical SSO."
|
||||
32`.
|
||||
<div class="height:1px;"/>
|
||||
- `SITE_URL`: The absolute URL of your self-hosted instance of Infisical including the protocol (e.g. https://app.infisical.com)
|
||||
</Note>
|
||||
</Note>
|
||||
|
||||
@@ -5,7 +5,7 @@ description: "Learn how to configure Microsoft Entra ID for Infisical SSO."
|
||||
|
||||
<Info>
|
||||
Azure SAML SSO is a paid feature.
|
||||
|
||||
|
||||
If you're using Infisical Cloud, then it is available under the **Pro Tier**. If you're self-hosting Infisical,
|
||||
then you should contact sales@infisical.com to purchase an enterprise license to use it.
|
||||
</Info>
|
||||
@@ -26,7 +26,7 @@ description: "Learn how to configure Microsoft Entra ID for Infisical SSO."
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
On the next screen, press the **+ Create your own application** button.
|
||||
Give the application a unique name like Infisical; choose the "Integrate any other application you don't find in the gallery (Non-gallery)"
|
||||
option and hit the **Create** button.
|
||||
@@ -89,9 +89,9 @@ description: "Learn how to configure Microsoft Entra ID for Infisical SSO."
|
||||
</Step>
|
||||
<Step title="Assign users in Azure to the application">
|
||||
Back in Azure, navigate to the **Users and groups** tab and select **+ Add user/group** to assign access to the login with SSO application on a user or group-level.
|
||||
|
||||
|
||||

|
||||
</Step>
|
||||
</Step>
|
||||
<Step title="Enable SAML SSO in Infisical">
|
||||
Enabling SAML SSO allows members in your organization to log into Infisical via Azure.
|
||||
|
||||
@@ -109,7 +109,7 @@ description: "Learn how to configure Microsoft Entra ID for Infisical SSO."
|
||||
prior to enforcing SAML SSO to prevent any unintended issues.
|
||||
</Warning>
|
||||
<Info>
|
||||
In case of a lockout, an organization admin can use the admin login portal in the `/login/admin` path e.g. https://app.infisical.com/login/admin.
|
||||
In case of a lockout, an organization admin can use the [Admin Login Portal](https://infisical.com/docs/documentation/platform/sso/overview#admin-login-portal) in the `/login/admin` path e.g. https://app.infisical.com/login/admin.
|
||||
</Info>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
@@ -70,7 +70,7 @@ Prerequisites:
|
||||
We recommend ensuring that your account is provisioned using the identity provider prior to enforcing OIDC SSO to prevent any unintended issues.
|
||||
</Warning>
|
||||
<Info>
|
||||
In case of a lockout, an organization admin can use the admin login portal in the `/login/admin` path e.g. https://app.infisical.com/login/admin.
|
||||
In case of a lockout, an organization admin can use the [Admin Login Portal](https://infisical.com/docs/documentation/platform/sso/overview#admin-login-portal) in the `/login/admin` path e.g. https://app.infisical.com/login/admin.
|
||||
</Info>
|
||||
</Step>
|
||||
|
||||
|
||||
@@ -24,21 +24,21 @@ description: "Learn how to configure Google SAML for Infisical SSO."
|
||||
<Step title="Create a SAML application in Google">
|
||||
2.1. In your [Google Admin console](https://support.google.com/a/answer/182076), head to Menu > Apps > Web and mobile apps and
|
||||
create a **custom SAML app**.
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
2.2. In the **App details** tab, give the application a unique name like Infisical.
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
2.3. In the **Google Identity Provider details** tab, copy the **SSO URL**, **Entity ID** and **Certificate**.
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
2.4. Back in Infisical, set **SSO URL** and **Certificate** to the corresponding items from step 2.3.
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
2.5. Back in the Google Admin console, in the **Service provider details** tab, set the **ACS URL** and **Entity ID** to the corresponding items from step 1.
|
||||
|
||||
Also, check the **Signed response** checkbox.
|
||||
@@ -84,7 +84,7 @@ description: "Learn how to configure Google SAML for Infisical SSO."
|
||||
prior to enforcing SAML SSO to prevent any unintended issues.
|
||||
</Warning>
|
||||
<Info>
|
||||
In case of a lockout, an organization admin can use the admin login portal in the `/login/admin` path e.g. https://app.infisical.com/login/admin.
|
||||
In case of a lockout, an organization admin can use the [Admin Login Portal](https://infisical.com/docs/documentation/platform/sso/overview#admin-login-portal) in the `/login/admin` path e.g. https://app.infisical.com/login/admin.
|
||||
</Info>
|
||||
</Step>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ description: "Learn how to configure JumpCloud SAML for Infisical SSO."
|
||||
|
||||
<Info>
|
||||
JumpCloud SAML SSO is a paid feature.
|
||||
|
||||
|
||||
If you're using Infisical Cloud, then it is available under the **Pro Tier**. If you're self-hosting Infisical,
|
||||
then you should contact sales@infisical.com to purchase an enterprise license to use it.
|
||||
</Info>
|
||||
@@ -83,13 +83,12 @@ description: "Learn how to configure JumpCloud SAML for Infisical SSO."
|
||||
|
||||
To enforce SAML SSO, you're required to test out the SAML connection by successfully authenticating at least one JumpCloud user with Infisical;
|
||||
Once you've completed this requirement, you can toggle the **Enforce SAML SSO** button to enforce SAML SSO.
|
||||
|
||||
|
||||
<Warning>
|
||||
We recommend ensuring that your account is provisioned the application in JumpCloud
|
||||
prior to enforcing SAML SSO to prevent any unintended issues.
|
||||
We recommend ensuring that your account is provisioned in the application in JumpCloud prior to enforcing SAML SSO to prevent any unintended issues.
|
||||
</Warning>
|
||||
<Info>
|
||||
In case of a lockout, an organization admin can use the admin login portal in the `/login/admin` path e.g. https://app.infisical.com/login/admin.
|
||||
In case of a lockout, an organization admin can use the [Admin Login Portal](https://infisical.com/docs/documentation/platform/sso/overview#admin-login-portal) in the `/login/admin` path e.g. https://app.infisical.com/login/admin.
|
||||
</Info>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
@@ -97,7 +97,7 @@ description: "Learn how to configure Keycloak OIDC for Infisical SSO."
|
||||
prior to enforcing OIDC SSO to prevent any unintended issues.
|
||||
</Warning>
|
||||
<Info>
|
||||
In case of a lockout, an organization admin can use the admin login portal in the `/login/admin` path e.g. https://app.infisical.com/login/admin.
|
||||
In case of a lockout, an organization admin can use the [Admin Login Portal](https://infisical.com/docs/documentation/platform/sso/overview#admin-login-portal) in the `/login/admin` path e.g. https://app.infisical.com/login/admin.
|
||||
</Info>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
@@ -5,7 +5,7 @@ description: "Learn how to configure Keycloak SAML for Infisical SSO."
|
||||
|
||||
<Info>
|
||||
Keycloak SAML SSO is a paid feature.
|
||||
|
||||
|
||||
If you're using Infisical Cloud, then it is available under the **Pro Tier**. If you're self-hosting Infisical,
|
||||
then you should contact sales@infisical.com to purchase an enterprise license to use it.
|
||||
</Info>
|
||||
@@ -13,36 +13,36 @@ description: "Learn how to configure Keycloak SAML for Infisical SSO."
|
||||
<Steps>
|
||||
<Step title="Prepare the SAML SSO configuration in Infisical">
|
||||
In Infisical, head to the **Single Sign-On (SSO)** page and select the **General** tab. Click **Connect** for **SAML** under the Connect to an Identity Provider section. Select **Keycloak**, then click **Connect** again.
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
Next, copy the **Valid redirect URI** and **SP Entity ID** to use when configuring the Keycloak SAML application.
|
||||
|
||||
|
||||

|
||||
</Step>
|
||||
<Step title="Create a SAML client application in Keycloak">
|
||||
2.1. In your realm, navigate to the **Clients** tab and click **Create client** to create a new client application.
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
<Info>
|
||||
You don’t typically need to make a realm dedicated to Infisical. We recommend adding Infisical as a client to your primary realm.
|
||||
</Info>
|
||||
|
||||
|
||||
In the General Settings step, set **Client type** to **SAML**, the **Client ID** field to `https://app.infisical.com`, and the **Name** field to a friendly name like **Infisical**.
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
<Info>
|
||||
If you’re self-hosting Infisical, then you will want to replace https://app.infisical.com with your own domain.
|
||||
</Info>
|
||||
|
||||
|
||||
Next, in the Login Settings step, set both the **Home URL** field and **Valid redirect URIs** field to the **Valid redirect URI** from step 1 and press **Save**.
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
2.2. Once you've created the client, under its **Settings** tab, make sure to set the following values:
|
||||
|
||||
|
||||
- Under **SAML Capabilities**:
|
||||
- Name ID format: email (or username).
|
||||
- Force name ID format: On.
|
||||
@@ -54,59 +54,59 @@ description: "Learn how to configure Keycloak SAML for Infisical SSO."
|
||||
- Signature algorithm: RSA_SHA256.
|
||||
|
||||

|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
2.3. Next, navigate to the **Client scopes** tab select the client's dedicated scope.
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
Next click **Add predefined mapper**.
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
Select the **X500 email**, **X500 givenName**, and **X500 surname** attributes and click **Add**.
|
||||
|
||||
|
||||

|
||||
|
||||
Now click on the **X500 email** mapper and set the **SAML Attribute Name** field to **email**.
|
||||
|
||||
Now click on the **X500 email** mapper and set the **SAML Attribute Name** field to **email**.
|
||||
|
||||

|
||||
|
||||
|
||||
Repeat the same for **X500 givenName** and **X500 surname** mappers, setting the **SAML Attribute Name** field to **firstName** and **lastName** respectively.
|
||||
|
||||
|
||||
Next, back in the client scope's **Mappers**, click **Add mapper** and select **by configuration**.
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
Select **User Property**.
|
||||
|
||||
|
||||

|
||||
|
||||
Set the the **Name** field to **Username**, the **Property** field to **username**, and the **SAML Attribtue Name** to **username**.
|
||||
|
||||
Set the the **Name** field to **Username**, the **Property** field to **username**, and the **SAML Attribute Name** to **username**.
|
||||
|
||||

|
||||
|
||||
|
||||
Repeat the same for the `id` attribute, setting the **Name** field to **ID**, the **Property** field to **id**, and the **SAML Attribute Name** to **id**.
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
Once you've completed the above steps, the list of mappers should look like this:
|
||||
|
||||
|
||||

|
||||
</Step>
|
||||
<Step title="Retrieve Identity Provider (IdP) Information from Keycloak">
|
||||
Back in Keycloak, navigate to Configure > Realm settings > General tab > Endpoints > SAML 2.0 Identity Provider Metadata and copy the IDP URL. This should appear in various places and take the form: `https://keycloak-mysite.com/realms/myrealm/protocol/saml`.
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
Also, in the **Keys** tab, locate the RS256 key and copy the certificate to use when finishing configuring Keycloak SAML in Infisical.
|
||||
|
||||
|
||||

|
||||
</Step>
|
||||
<Step title="Finish configuring SAML in Infisical">
|
||||
Back in Infisical, set **IDP URL** and **Certificate** to the items from step 3. Also, set the **Client ID** to the `https://app.infisical.com`.
|
||||
|
||||
|
||||
Once you've done that, press **Update** to complete the required configuration.
|
||||
|
||||

|
||||
@@ -119,7 +119,7 @@ description: "Learn how to configure Keycloak SAML for Infisical SSO."
|
||||
<Step title="Enforce SAML SSO in Infisical">
|
||||
Enforcing SAML SSO ensures that members in your organization can only access Infisical
|
||||
by logging into the organization via Keycloak.
|
||||
|
||||
|
||||
To enforce SAML SSO, you're required to test out the SAML connection by successfully authenticating at least one Keycloak user with Infisical;
|
||||
Once you've completed this requirement, you can toggle the **Enforce SAML SSO** button to enforce SAML SSO.
|
||||
|
||||
@@ -128,7 +128,7 @@ description: "Learn how to configure Keycloak SAML for Infisical SSO."
|
||||
prior to enforcing SAML SSO to prevent any unintended issues.
|
||||
</Warning>
|
||||
<Info>
|
||||
In case of a lockout, an organization admin can use the admin login portal in the `/login/admin` path e.g. https://app.infisical.com/login/admin.
|
||||
In case of a lockout, an organization admin can use the [Admin Login Portal](https://infisical.com/docs/documentation/platform/sso/overview#admin-login-portal) in the `/login/admin` path e.g. https://app.infisical.com/login/admin.
|
||||
</Info>
|
||||
</Step>
|
||||
</Steps>
|
||||
@@ -147,4 +147,4 @@ description: "Learn how to configure Keycloak SAML for Infisical SSO."
|
||||
32`.
|
||||
<div class="height:1px;"/>
|
||||
- `SITE_URL`: The absolute URL of your self-hosted instance of Infisical including the protocol (e.g. https://app.infisical.com)
|
||||
</Note>
|
||||
</Note>
|
||||
|
||||
@@ -93,13 +93,12 @@ description: "Learn how to configure Okta SAML 2.0 for Infisical SSO."
|
||||
Once you've completed this requirement, you can toggle the **Enforce SAML SSO** button to enforce SAML SSO.
|
||||
|
||||
<Warning>
|
||||
We recommend ensuring that your account is provisioned the application in Okta
|
||||
prior to enforcing SAML SSO to prevent any unintended issues.
|
||||
We recommend ensuring that your account is provisioned for the application in Okta prior to enforcing SAML SSO to prevent any unintended issues.
|
||||
</Warning>
|
||||
<Info>
|
||||
In case of a lockout, an organization admin can use the admin login portal in the `/login/admin` path e.g. https://app.infisical.com/login/admin.
|
||||
</Info>
|
||||
|
||||
<Info>
|
||||
In case of a lockout, an organization admin can use the [Admin Login Portal](https://infisical.com/docs/documentation/platform/sso/overview#admin-login-portal) in the `/login/admin` path e.g. https://app.infisical.com/login/admin.
|
||||
</Info>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
|
||||
@@ -39,18 +39,30 @@ If your required identity provider is not shown in the list above, please reach
|
||||
For enhanced security, Infisical enforces PKCE (Proof Key for Code Exchange) with the OAuth 2.0-based SSO providers and OIDC. This provides additional protection against authorization code interception attacks and strengthens your authentication flow security.
|
||||
</Info>
|
||||
|
||||
## SSO Break Glass
|
||||
|
||||
In the event your SSO provider experiences downtime, and you need to access Infisical, Organization Admins can utilize the Admin Login Portal to bypass SSO enforcement.
|
||||
|
||||
This portal is accessible at `/login/admin` (e.g., https://app.infisical.com/login/admin).
|
||||
|
||||
<Note>
|
||||
To bypass SSO for an organization, you must be an **Organization Admin** for that specific organization. This **Organization Admin** role is independent of **Server Admin** status. Being a **Server Admin** alone does not grant permission to use this bypass feature.
|
||||
</Note>
|
||||
|
||||
## FAQ
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Why does Infisical require additional email verification for users connected via SAML?">
|
||||
By default, Infisical Cloud is configured to not trust emails from external
|
||||
identity providers to prevent any malicious account takeover attempts via
|
||||
email spoofing. Accordingly, Infisical creates a new user for anyone provisioned
|
||||
through an external identity provider and requires an additional email
|
||||
verification step upon their first login.
|
||||
<Accordion title="Why does Infisical require additional email verification for users connected via SAML?">
|
||||
By default, Infisical Cloud is configured to not trust emails from external
|
||||
identity providers to prevent any malicious account takeover attempts via
|
||||
email spoofing. Accordingly, Infisical creates a new user for anyone provisioned
|
||||
through an external identity provider and requires an additional email
|
||||
verification step upon their first login.
|
||||
|
||||
If you're running a self-hosted instance of Infisical and would like it to trust emails from external identity providers,
|
||||
you can configure this behavior in the Server Admin Console.
|
||||
|
||||
</Accordion>
|
||||
If you're running a self-hosted instance of Infisical and would like it to trust emails from external identity providers,
|
||||
you can configure this behavior in the Server Admin Console.
|
||||
</Accordion>
|
||||
<Accordion title="Why do I get redirected to SSO when trying to use the Admin Login Portal?">
|
||||
You are likely being redirected because you do not have email authentication mode enabled, or you're not an **Organization Admin**. This portal requires **Organization Admin** status and direct credential login (email and password). **Server Admin** status alone is insufficient.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -3,6 +3,13 @@ title: "OCI Connection"
|
||||
description: "Learn how to configure an Oracle Cloud Infrastructure Connection for Infisical."
|
||||
---
|
||||
|
||||
<Info>
|
||||
OCI App Connection is a paid feature.
|
||||
|
||||
If you're using Infisical Cloud, then it is available under the **Enterprise Tier**. If you're self-hosting Infisical,
|
||||
then you should contact team@infisical.com to purchase an enterprise license to use it.
|
||||
</Info>
|
||||
|
||||
Infisical supports the use of [API Signing Key Authentication](https://docs.oracle.com/en-us/iaas/Content/API/Concepts/apisigningkey.htm) to connect with OCI.
|
||||
|
||||
## Create OCI User
|
||||
|
||||
@@ -3,6 +3,13 @@ title: "OCI Vault Sync"
|
||||
description: "Learn how to configure an Oracle Cloud Infrastructure Vault Sync for Infisical."
|
||||
---
|
||||
|
||||
<Info>
|
||||
OCI Vault Sync is a paid feature.
|
||||
|
||||
If you're using Infisical Cloud, then it is available under the **Enterprise Tier**. If you're self-hosting Infisical,
|
||||
then you should contact team@infisical.com to purchase an enterprise license to use it.
|
||||
</Info>
|
||||
|
||||
**Prerequisites:**
|
||||
- Create an [OCI Connection](/integrations/app-connections/oci) with the required **Secret Sync** permissions
|
||||
- [Create](https://docs.oracle.com/en-us/iaas/Content/Identity/compartments/To_create_a_compartment.htm) or use an existing OCI Compartment (which the OCI Connection is authorized to access)
|
||||
|
||||
@@ -2,16 +2,23 @@ import { faWrench } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { Spinner, Tooltip } from "@app/components/v2";
|
||||
import { useSubscription } from "@app/context";
|
||||
import { SECRET_SYNC_MAP } from "@app/helpers/secretSyncs";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import { SecretSync, useSecretSyncOptions } from "@app/hooks/api/secretSyncs";
|
||||
|
||||
import { UpgradePlanModal } from "../license/UpgradePlanModal";
|
||||
|
||||
type Props = {
|
||||
onSelect: (destination: SecretSync) => void;
|
||||
};
|
||||
|
||||
export const SecretSyncSelect = ({ onSelect }: Props) => {
|
||||
const { subscription } = useSubscription();
|
||||
const { isPending, data: secretSyncOptions } = useSecretSyncOptions();
|
||||
|
||||
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"] as const);
|
||||
|
||||
if (isPending) {
|
||||
return (
|
||||
<div className="flex h-full flex-col items-center justify-center py-2.5">
|
||||
@@ -23,14 +30,17 @@ export const SecretSyncSelect = ({ onSelect }: Props) => {
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-4 gap-2">
|
||||
{secretSyncOptions?.map(({ destination }) => {
|
||||
{secretSyncOptions?.map(({ destination, enterprise }) => {
|
||||
const { image, name } = SECRET_SYNC_MAP[destination];
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
key={destination}
|
||||
onClick={() => onSelect(destination)}
|
||||
className="group relative flex h-28 cursor-pointer flex-col items-center justify-center rounded-md border border-mineshaft-600 bg-mineshaft-700 p-4 duration-200 hover:bg-mineshaft-600"
|
||||
onClick={() =>
|
||||
enterprise && !subscription.enterpriseSecretSyncs
|
||||
? handlePopUpOpen("upgradePlan")
|
||||
: onSelect(destination)
|
||||
}
|
||||
className="group relative flex h-28 cursor-pointer flex-col items-center justify-center overflow-hidden rounded-md border border-mineshaft-600 bg-mineshaft-700 p-4 duration-200 hover:bg-mineshaft-600"
|
||||
>
|
||||
<img
|
||||
src={`/images/integrations/${image}`}
|
||||
@@ -45,6 +55,11 @@ export const SecretSyncSelect = ({ onSelect }: Props) => {
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
<UpgradePlanModal
|
||||
isOpen={popUp.upgradePlan.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||
text="You can use every Secret Sync if you switch to Infisical's Enterprise plan."
|
||||
/>
|
||||
<Tooltip
|
||||
side="bottom"
|
||||
className="max-w-sm py-4"
|
||||
|
||||
@@ -35,7 +35,7 @@ import { OCIConnectionMethod } from "@app/hooks/api/appConnections/types/oci-con
|
||||
|
||||
export const APP_CONNECTION_MAP: Record<
|
||||
AppConnection,
|
||||
{ name: string; image: string; size?: number }
|
||||
{ name: string; image: string; size?: number; enterprise?: boolean }
|
||||
> = {
|
||||
[AppConnection.AWS]: { name: "AWS", image: "Amazon Web Services.png" },
|
||||
[AppConnection.GitHub]: { name: "GitHub", image: "GitHub.png" },
|
||||
@@ -65,7 +65,7 @@ export const APP_CONNECTION_MAP: Record<
|
||||
[AppConnection.HCVault]: { name: "Hashicorp Vault", image: "Vault.png", size: 65 },
|
||||
[AppConnection.LDAP]: { name: "LDAP", image: "LDAP.png", size: 65 },
|
||||
[AppConnection.TeamCity]: { name: "TeamCity", image: "TeamCity.png" },
|
||||
[AppConnection.OCI]: { name: "OCI", image: "Oracle.png" }
|
||||
[AppConnection.OCI]: { name: "OCI", image: "Oracle.png", enterprise: true }
|
||||
};
|
||||
|
||||
export const getAppConnectionMethodDetails = (method: TAppConnection["method"]) => {
|
||||
|
||||
@@ -21,6 +21,7 @@ export type TSecretSyncOption = {
|
||||
name: string;
|
||||
destination: SecretSync;
|
||||
canImportSecrets: boolean;
|
||||
enterprise?: boolean;
|
||||
};
|
||||
|
||||
export type TSecretSync =
|
||||
|
||||
@@ -50,4 +50,6 @@ export type SubscriptionPlan = {
|
||||
enforceMfa: boolean;
|
||||
projectTemplates: boolean;
|
||||
kmip: boolean;
|
||||
enterpriseSecretSyncs: boolean;
|
||||
enterpriseAppConnections: boolean;
|
||||
};
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { faWrench } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
|
||||
import { Spinner, Tooltip } from "@app/components/v2";
|
||||
import { useSubscription } from "@app/context";
|
||||
import { APP_CONNECTION_MAP } from "@app/helpers/appConnections";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import { useAppConnectionOptions } from "@app/hooks/api/appConnections";
|
||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||
|
||||
@@ -11,8 +14,11 @@ type Props = {
|
||||
};
|
||||
|
||||
export const AppConnectionsSelect = ({ onSelect }: Props) => {
|
||||
const { subscription } = useSubscription();
|
||||
const { isPending, data: appConnectionOptions } = useAppConnectionOptions();
|
||||
|
||||
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"] as const);
|
||||
|
||||
if (isPending) {
|
||||
return (
|
||||
<div className="flex h-full flex-col items-center justify-center py-2.5">
|
||||
@@ -25,13 +31,16 @@ export const AppConnectionsSelect = ({ onSelect }: Props) => {
|
||||
return (
|
||||
<div className="grid grid-cols-4 gap-2">
|
||||
{appConnectionOptions?.map((option) => {
|
||||
const { image, name, size = 50 } = APP_CONNECTION_MAP[option.app];
|
||||
const { image, name, size = 50, enterprise = false } = APP_CONNECTION_MAP[option.app];
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
key={option.app}
|
||||
onClick={() => onSelect(option.app)}
|
||||
onClick={() =>
|
||||
enterprise && !subscription.enterpriseAppConnections
|
||||
? handlePopUpOpen("upgradePlan")
|
||||
: onSelect(option.app)
|
||||
}
|
||||
className="group relative flex h-28 cursor-pointer flex-col items-center justify-center rounded-md border border-mineshaft-600 bg-mineshaft-700 p-4 duration-200 hover:bg-mineshaft-600"
|
||||
>
|
||||
<img
|
||||
@@ -48,6 +57,11 @@ export const AppConnectionsSelect = ({ onSelect }: Props) => {
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
<UpgradePlanModal
|
||||
isOpen={popUp.upgradePlan.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||
text="You can use every App Connection if you switch to Infisical's Enterprise plan."
|
||||
/>
|
||||
<Tooltip
|
||||
side="bottom"
|
||||
className="max-w-sm py-4"
|
||||
|
||||
@@ -129,7 +129,16 @@ export const OrgGeneralAuthSection = () => {
|
||||
level.
|
||||
</span>
|
||||
<p className="mt-4">
|
||||
In case of a lockout, admins can use the admin login portal at{" "}
|
||||
In case of a lockout, admins can use the{" "}
|
||||
<a
|
||||
target="_blank"
|
||||
className="underline underline-offset-2 hover:text-mineshaft-300"
|
||||
href="https://infisical.com/docs/documentation/platform/sso/overview#admin-login-portal"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Admin Login Portal
|
||||
</a>{" "}
|
||||
at{" "}
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
|
||||
@@ -212,7 +212,16 @@ export const OrgOIDCSection = (): JSX.Element => {
|
||||
level.
|
||||
</span>
|
||||
<p className="mt-4">
|
||||
In case of a lockout, admins can use the admin login portal at{" "}
|
||||
In case of a lockout, admins can use the{" "}
|
||||
<a
|
||||
target="_blank"
|
||||
className="underline underline-offset-2 hover:text-mineshaft-300"
|
||||
href="https://infisical.com/docs/documentation/platform/sso/overview#admin-login-portal"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Admin Login Portal
|
||||
</a>{" "}
|
||||
at{" "}
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
|
||||
Reference in New Issue
Block a user