Compare commits

...

21 Commits

Author SHA1 Message Date
0191eb48f3 Merge pull request #3974 from Infisical/fix-email-invite-notifications
Improve + fix invitation reminder logic
2025-07-07 14:47:50 -04:00
9d39910152 Minor fix to prevent setting lastInvitedAt for invitees who weren’t actually sent an invitation 2025-07-07 15:35:49 -03:00
9137fa4ca5 Improve + fix invitation reminder logic 2025-07-07 13:31:20 -04:00
78da7ec343 Merge pull request #3972 from Infisical/fix/telemetryOrgIdentify
feat(telemetry): improve Posthog org identity logic
2025-07-07 10:15:59 -03:00
a678ebb4ac Fix Cloud telemetry queue initialization 2025-07-07 10:10:30 -03:00
83dd38db49 feat(telemetry): reduce TELEMETRY_AGGREGATED_KEY_EXP to 10 mins and avoid sending org identitfy events for batch events on sendPostHogEvents 2025-07-07 08:36:15 -03:00
06f5af1200 Merge pull request #3890 from Infisical/daniel/sso-endpoints-docs
docs(api-reference/organizations): document SSO configuration endpoints
2025-07-04 05:33:52 +04:00
f903e5b3d4 Update saml-router.ts 2025-07-04 05:23:05 +04:00
c6f8915d3f Update saml-config-service.ts 2025-07-04 05:21:54 +04:00
65b1354ef1 fix: remove undefined return type from get saml endpoint 2025-07-04 05:07:54 +04:00
cda8579ca4 fix: requested changes 2025-07-04 04:51:14 +04:00
1b1acdcb0b Merge pull request #3917 from Infisical/cli-add-bitbucket-platform
Add BitBucket platform to secret scanning
2025-07-03 20:06:48 -04:00
a8f08730a1 Merge pull request #3908 from Infisical/fix/ui-small-catches
feat: added autoplay to loading lottie and fixed tooltip in project select
2025-07-03 19:35:59 -04:00
42648a134c Update utils.go to look more like Gitleaks version 2025-07-03 12:47:25 -04:00
23b20ebdab Fix CLI always defaulting to github 2025-07-03 00:49:31 -04:00
37d490ede3 Add BitBucket platform to secret scanning 2025-07-03 00:09:28 -04:00
=
7ab67db84d feat: fixed black color in tooltip 2025-07-03 01:18:52 +05:30
=
3a17281e37 feat: resolved tooltip overflow 2025-07-03 00:41:47 +05:30
=
abfe185a5b feat: added autoplay to loading lottie and fixed tooltip in project select 2025-07-02 22:13:37 +05:30
13d2cbd8b0 Update docs.json 2025-07-01 02:09:14 +04:00
abfc5736fd docs(api-reference/organizations): document SSO configuration endpoints 2025-07-01 02:05:53 +04:00
39 changed files with 523 additions and 205 deletions

View File

@ -0,0 +1,21 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasColumn = await knex.schema.hasColumn(TableName.OrgMembership, "lastInvitedAt");
if (hasColumn) {
await knex.schema.alterTable(TableName.OrgMembership, (t) => {
t.datetime("lastInvitedAt").nullable().defaultTo(knex.fn.now()).alter();
});
}
}
export async function down(knex: Knex): Promise<void> {
const hasColumn = await knex.schema.hasColumn(TableName.OrgMembership, "lastInvitedAt");
if (hasColumn) {
await knex.schema.alterTable(TableName.OrgMembership, (t) => {
t.datetime("lastInvitedAt").nullable().alter();
});
}
}

View File

@ -17,6 +17,7 @@ import { z } from "zod";
import { LdapGroupMapsSchema } from "@app/db/schemas";
import { TLDAPConfig } from "@app/ee/services/ldap-config/ldap-config-types";
import { isValidLdapFilter, searchGroups } from "@app/ee/services/ldap-config/ldap-fns";
import { ApiDocsTags, LdapSso } from "@app/lib/api-docs";
import { getConfig } from "@app/lib/config/env";
import { BadRequestError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
@ -132,10 +133,18 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.LdapSso],
description: "Get LDAP config",
security: [
{
bearerAuth: []
}
],
querystring: z.object({
organizationId: z.string().trim()
organizationId: z.string().trim().describe(LdapSso.GET_CONFIG.organizationId)
}),
response: {
200: z.object({
@ -172,23 +181,32 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.LdapSso],
description: "Create LDAP config",
security: [
{
bearerAuth: []
}
],
body: z.object({
organizationId: z.string().trim(),
isActive: z.boolean(),
url: z.string().trim(),
bindDN: z.string().trim(),
bindPass: z.string().trim(),
uniqueUserAttribute: z.string().trim().default("uidNumber"),
searchBase: z.string().trim(),
searchFilter: z.string().trim().default("(uid={{username}})"),
groupSearchBase: z.string().trim(),
organizationId: z.string().trim().describe(LdapSso.CREATE_CONFIG.organizationId),
isActive: z.boolean().describe(LdapSso.CREATE_CONFIG.isActive),
url: z.string().trim().describe(LdapSso.CREATE_CONFIG.url),
bindDN: z.string().trim().describe(LdapSso.CREATE_CONFIG.bindDN),
bindPass: z.string().trim().describe(LdapSso.CREATE_CONFIG.bindPass),
uniqueUserAttribute: z.string().trim().default("uidNumber").describe(LdapSso.CREATE_CONFIG.uniqueUserAttribute),
searchBase: z.string().trim().describe(LdapSso.CREATE_CONFIG.searchBase),
searchFilter: z.string().trim().default("(uid={{username}})").describe(LdapSso.CREATE_CONFIG.searchFilter),
groupSearchBase: z.string().trim().describe(LdapSso.CREATE_CONFIG.groupSearchBase),
groupSearchFilter: z
.string()
.trim()
.default("(|(memberUid={{.Username}})(member={{.UserDN}})(uniqueMember={{.UserDN}}))"),
caCert: z.string().trim().default("")
.default("(|(memberUid={{.Username}})(member={{.UserDN}})(uniqueMember={{.UserDN}}))")
.describe(LdapSso.CREATE_CONFIG.groupSearchFilter),
caCert: z.string().trim().default("").describe(LdapSso.CREATE_CONFIG.caCert)
}),
response: {
200: SanitizedLdapConfigSchema
@ -214,23 +232,31 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.LdapSso],
description: "Update LDAP config",
security: [
{
bearerAuth: []
}
],
body: z
.object({
isActive: z.boolean(),
url: z.string().trim(),
bindDN: z.string().trim(),
bindPass: z.string().trim(),
uniqueUserAttribute: z.string().trim(),
searchBase: z.string().trim(),
searchFilter: z.string().trim(),
groupSearchBase: z.string().trim(),
groupSearchFilter: z.string().trim(),
caCert: z.string().trim()
isActive: z.boolean().describe(LdapSso.UPDATE_CONFIG.isActive),
url: z.string().trim().describe(LdapSso.UPDATE_CONFIG.url),
bindDN: z.string().trim().describe(LdapSso.UPDATE_CONFIG.bindDN),
bindPass: z.string().trim().describe(LdapSso.UPDATE_CONFIG.bindPass),
uniqueUserAttribute: z.string().trim().describe(LdapSso.UPDATE_CONFIG.uniqueUserAttribute),
searchBase: z.string().trim().describe(LdapSso.UPDATE_CONFIG.searchBase),
searchFilter: z.string().trim().describe(LdapSso.UPDATE_CONFIG.searchFilter),
groupSearchBase: z.string().trim().describe(LdapSso.UPDATE_CONFIG.groupSearchBase),
groupSearchFilter: z.string().trim().describe(LdapSso.UPDATE_CONFIG.groupSearchFilter),
caCert: z.string().trim().describe(LdapSso.UPDATE_CONFIG.caCert)
})
.partial()
.merge(z.object({ organizationId: z.string() })),
.merge(z.object({ organizationId: z.string().trim().describe(LdapSso.UPDATE_CONFIG.organizationId) })),
response: {
200: SanitizedLdapConfigSchema
}

View File

@ -13,6 +13,7 @@ import { z } from "zod";
import { OidcConfigsSchema } from "@app/db/schemas";
import { OIDCConfigurationType, OIDCJWTSignatureAlgorithm } from "@app/ee/services/oidc/oidc-config-types";
import { ApiDocsTags, OidcSSo } from "@app/lib/api-docs";
import { getConfig } from "@app/lib/config/env";
import { authRateLimit, readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
@ -153,10 +154,18 @@ export const registerOidcRouter = async (server: FastifyZodProvider) => {
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.OidcSso],
description: "Get OIDC config",
security: [
{
bearerAuth: []
}
],
querystring: z.object({
orgSlug: z.string().trim()
organizationId: z.string().trim().describe(OidcSSo.GET_CONFIG.organizationId)
}),
response: {
200: SanitizedOidcConfigSchema.pick({
@ -180,9 +189,8 @@ export const registerOidcRouter = async (server: FastifyZodProvider) => {
}
},
handler: async (req) => {
const { orgSlug } = req.query;
const oidc = await server.services.oidc.getOidc({
orgSlug,
organizationId: req.query.organizationId,
type: "external",
actor: req.permission.type,
actorId: req.permission.id,
@ -200,8 +208,16 @@ export const registerOidcRouter = async (server: FastifyZodProvider) => {
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.OidcSso],
description: "Update OIDC config",
security: [
{
bearerAuth: []
}
],
body: z
.object({
allowedEmailDomains: z
@ -216,22 +232,26 @@ export const registerOidcRouter = async (server: FastifyZodProvider) => {
.split(",")
.map((id) => id.trim())
.join(", ");
}),
discoveryURL: z.string().trim(),
configurationType: z.nativeEnum(OIDCConfigurationType),
issuer: z.string().trim(),
authorizationEndpoint: z.string().trim(),
jwksUri: z.string().trim(),
tokenEndpoint: z.string().trim(),
userinfoEndpoint: z.string().trim(),
clientId: z.string().trim(),
clientSecret: z.string().trim(),
isActive: z.boolean(),
manageGroupMemberships: z.boolean().optional(),
jwtSignatureAlgorithm: z.nativeEnum(OIDCJWTSignatureAlgorithm).optional()
})
.describe(OidcSSo.UPDATE_CONFIG.allowedEmailDomains),
discoveryURL: z.string().trim().describe(OidcSSo.UPDATE_CONFIG.discoveryURL),
configurationType: z.nativeEnum(OIDCConfigurationType).describe(OidcSSo.UPDATE_CONFIG.configurationType),
issuer: z.string().trim().describe(OidcSSo.UPDATE_CONFIG.issuer),
authorizationEndpoint: z.string().trim().describe(OidcSSo.UPDATE_CONFIG.authorizationEndpoint),
jwksUri: z.string().trim().describe(OidcSSo.UPDATE_CONFIG.jwksUri),
tokenEndpoint: z.string().trim().describe(OidcSSo.UPDATE_CONFIG.tokenEndpoint),
userinfoEndpoint: z.string().trim().describe(OidcSSo.UPDATE_CONFIG.userinfoEndpoint),
clientId: z.string().trim().describe(OidcSSo.UPDATE_CONFIG.clientId),
clientSecret: z.string().trim().describe(OidcSSo.UPDATE_CONFIG.clientSecret),
isActive: z.boolean().describe(OidcSSo.UPDATE_CONFIG.isActive),
manageGroupMemberships: z.boolean().optional().describe(OidcSSo.UPDATE_CONFIG.manageGroupMemberships),
jwtSignatureAlgorithm: z
.nativeEnum(OIDCJWTSignatureAlgorithm)
.optional()
.describe(OidcSSo.UPDATE_CONFIG.jwtSignatureAlgorithm)
})
.partial()
.merge(z.object({ orgSlug: z.string() })),
.merge(z.object({ organizationId: z.string().describe(OidcSSo.UPDATE_CONFIG.organizationId) })),
response: {
200: SanitizedOidcConfigSchema.pick({
id: true,
@ -267,8 +287,16 @@ export const registerOidcRouter = async (server: FastifyZodProvider) => {
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.OidcSso],
description: "Create OIDC config",
security: [
{
bearerAuth: []
}
],
body: z
.object({
allowedEmailDomains: z
@ -283,23 +311,34 @@ export const registerOidcRouter = async (server: FastifyZodProvider) => {
.split(",")
.map((id) => id.trim())
.join(", ");
}),
configurationType: z.nativeEnum(OIDCConfigurationType),
issuer: z.string().trim().optional().default(""),
discoveryURL: z.string().trim().optional().default(""),
authorizationEndpoint: z.string().trim().optional().default(""),
jwksUri: z.string().trim().optional().default(""),
tokenEndpoint: z.string().trim().optional().default(""),
userinfoEndpoint: z.string().trim().optional().default(""),
clientId: z.string().trim(),
clientSecret: z.string().trim(),
isActive: z.boolean(),
orgSlug: z.string().trim(),
manageGroupMemberships: z.boolean().optional().default(false),
})
.describe(OidcSSo.CREATE_CONFIG.allowedEmailDomains),
configurationType: z.nativeEnum(OIDCConfigurationType).describe(OidcSSo.CREATE_CONFIG.configurationType),
issuer: z.string().trim().optional().default("").describe(OidcSSo.CREATE_CONFIG.issuer),
discoveryURL: z.string().trim().optional().default("").describe(OidcSSo.CREATE_CONFIG.discoveryURL),
authorizationEndpoint: z
.string()
.trim()
.optional()
.default("")
.describe(OidcSSo.CREATE_CONFIG.authorizationEndpoint),
jwksUri: z.string().trim().optional().default("").describe(OidcSSo.CREATE_CONFIG.jwksUri),
tokenEndpoint: z.string().trim().optional().default("").describe(OidcSSo.CREATE_CONFIG.tokenEndpoint),
userinfoEndpoint: z.string().trim().optional().default("").describe(OidcSSo.CREATE_CONFIG.userinfoEndpoint),
clientId: z.string().trim().describe(OidcSSo.CREATE_CONFIG.clientId),
clientSecret: z.string().trim().describe(OidcSSo.CREATE_CONFIG.clientSecret),
isActive: z.boolean().describe(OidcSSo.CREATE_CONFIG.isActive),
organizationId: z.string().trim().describe(OidcSSo.CREATE_CONFIG.organizationId),
manageGroupMemberships: z
.boolean()
.optional()
.default(false)
.describe(OidcSSo.CREATE_CONFIG.manageGroupMemberships),
jwtSignatureAlgorithm: z
.nativeEnum(OIDCJWTSignatureAlgorithm)
.optional()
.default(OIDCJWTSignatureAlgorithm.RS256)
.describe(OidcSSo.CREATE_CONFIG.jwtSignatureAlgorithm)
})
.superRefine((data, ctx) => {
if (data.configurationType === OIDCConfigurationType.CUSTOM) {

View File

@ -13,6 +13,7 @@ import { FastifyRequest } from "fastify";
import { z } from "zod";
import { SamlProviders, TGetSamlCfgDTO } from "@app/ee/services/saml-config/saml-config-types";
import { ApiDocsTags, SamlSso } from "@app/lib/api-docs";
import { getConfig } from "@app/lib/config/env";
import { BadRequestError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
@ -149,8 +150,8 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
firstName,
lastName: lastName as string,
relayState: (req.body as { RelayState?: string }).RelayState,
authProvider: (req as unknown as FastifyRequest).ssoConfig?.authProvider as string,
orgId: (req as unknown as FastifyRequest).ssoConfig?.orgId as string,
authProvider: (req as unknown as FastifyRequest).ssoConfig?.authProvider,
orgId: (req as unknown as FastifyRequest).ssoConfig?.orgId,
metadata: userMetadata
});
cb(null, { isUserCompleted, providerAuthToken });
@ -262,25 +263,31 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.SamlSso],
description: "Get SAML config",
security: [
{
bearerAuth: []
}
],
querystring: z.object({
organizationId: z.string().trim()
organizationId: z.string().trim().describe(SamlSso.GET_CONFIG.organizationId)
}),
response: {
200: z
.object({
id: z.string(),
organization: z.string(),
orgId: z.string(),
authProvider: z.string(),
isActive: z.boolean(),
entryPoint: z.string(),
issuer: z.string(),
cert: z.string(),
lastUsed: z.date().nullable().optional()
})
.optional()
200: z.object({
id: z.string(),
organization: z.string(),
orgId: z.string(),
authProvider: z.string(),
isActive: z.boolean(),
entryPoint: z.string(),
issuer: z.string(),
cert: z.string(),
lastUsed: z.date().nullable().optional()
})
}
},
handler: async (req) => {
@ -302,15 +309,23 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.SamlSso],
description: "Create SAML config",
security: [
{
bearerAuth: []
}
],
body: z.object({
organizationId: z.string(),
authProvider: z.nativeEnum(SamlProviders),
isActive: z.boolean(),
entryPoint: z.string(),
issuer: z.string(),
cert: z.string()
organizationId: z.string().trim().describe(SamlSso.CREATE_CONFIG.organizationId),
authProvider: z.nativeEnum(SamlProviders).describe(SamlSso.CREATE_CONFIG.authProvider),
isActive: z.boolean().describe(SamlSso.CREATE_CONFIG.isActive),
entryPoint: z.string().trim().describe(SamlSso.CREATE_CONFIG.entryPoint),
issuer: z.string().trim().describe(SamlSso.CREATE_CONFIG.issuer),
cert: z.string().trim().describe(SamlSso.CREATE_CONFIG.cert)
}),
response: {
200: SanitizedSamlConfigSchema
@ -341,18 +356,26 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.SamlSso],
description: "Update SAML config",
security: [
{
bearerAuth: []
}
],
body: z
.object({
authProvider: z.nativeEnum(SamlProviders),
isActive: z.boolean(),
entryPoint: z.string(),
issuer: z.string(),
cert: z.string()
authProvider: z.nativeEnum(SamlProviders).describe(SamlSso.UPDATE_CONFIG.authProvider),
isActive: z.boolean().describe(SamlSso.UPDATE_CONFIG.isActive),
entryPoint: z.string().trim().describe(SamlSso.UPDATE_CONFIG.entryPoint),
issuer: z.string().trim().describe(SamlSso.UPDATE_CONFIG.issuer),
cert: z.string().trim().describe(SamlSso.UPDATE_CONFIG.cert)
})
.partial()
.merge(z.object({ organizationId: z.string() })),
.merge(z.object({ organizationId: z.string().trim().describe(SamlSso.UPDATE_CONFIG.organizationId) })),
response: {
200: SanitizedSamlConfigSchema
}

View File

@ -107,34 +107,26 @@ export const oidcConfigServiceFactory = ({
kmsService
}: TOidcConfigServiceFactoryDep) => {
const getOidc = async (dto: TGetOidcCfgDTO) => {
const org = await orgDAL.findOne({ slug: dto.orgSlug });
if (!org) {
const oidcCfg = await oidcConfigDAL.findOne({
orgId: dto.organizationId
});
if (!oidcCfg) {
throw new NotFoundError({
message: `Organization with slug '${dto.orgSlug}' not found`,
name: "OrgNotFound"
message: `OIDC configuration for organization with ID '${dto.organizationId}' not found`
});
}
if (dto.type === "external") {
const { permission } = await permissionService.getOrgPermission(
dto.actor,
dto.actorId,
org.id,
dto.organizationId,
dto.actorAuthMethod,
dto.actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Sso);
}
const oidcCfg = await oidcConfigDAL.findOne({
orgId: org.id
});
if (!oidcCfg) {
throw new NotFoundError({
message: `OIDC configuration for organization with slug '${dto.orgSlug}' not found`
});
}
const { decryptor } = await kmsService.createCipherPairWithDataKey({
type: KmsDataKey.Organization,
orgId: oidcCfg.orgId
@ -465,7 +457,7 @@ export const oidcConfigServiceFactory = ({
};
const updateOidcCfg = async ({
orgSlug,
organizationId,
allowedEmailDomains,
configurationType,
discoveryURL,
@ -484,13 +476,11 @@ export const oidcConfigServiceFactory = ({
manageGroupMemberships,
jwtSignatureAlgorithm
}: TUpdateOidcCfgDTO) => {
const org = await orgDAL.findOne({
slug: orgSlug
});
const org = await orgDAL.findOne({ id: organizationId });
if (!org) {
throw new NotFoundError({
message: `Organization with slug '${orgSlug}' not found`
message: `Organization with ID '${organizationId}' not found`
});
}
@ -555,7 +545,7 @@ export const oidcConfigServiceFactory = ({
};
const createOidcCfg = async ({
orgSlug,
organizationId,
allowedEmailDomains,
configurationType,
discoveryURL,
@ -574,12 +564,10 @@ export const oidcConfigServiceFactory = ({
manageGroupMemberships,
jwtSignatureAlgorithm
}: TCreateOidcCfgDTO) => {
const org = await orgDAL.findOne({
slug: orgSlug
});
const org = await orgDAL.findOne({ id: organizationId });
if (!org) {
throw new NotFoundError({
message: `Organization with slug '${orgSlug}' not found`
message: `Organization with ID '${organizationId}' not found`
});
}
@ -639,7 +627,7 @@ export const oidcConfigServiceFactory = ({
const oidcCfg = await getOidc({
type: "internal",
orgSlug
organizationId: org.id
});
if (!oidcCfg || !oidcCfg.isActive) {

View File

@ -26,11 +26,11 @@ export type TOidcLoginDTO = {
export type TGetOidcCfgDTO =
| ({
type: "external";
orgSlug: string;
organizationId: string;
} & TGenericPermission)
| {
type: "internal";
orgSlug: string;
organizationId: string;
};
export type TCreateOidcCfgDTO = {
@ -45,7 +45,7 @@ export type TCreateOidcCfgDTO = {
clientId: string;
clientSecret: string;
isActive: boolean;
orgSlug: string;
organizationId: string;
manageGroupMemberships: boolean;
jwtSignatureAlgorithm: OIDCJWTSignatureAlgorithm;
} & TGenericPermission;
@ -62,7 +62,7 @@ export type TUpdateOidcCfgDTO = Partial<{
clientId: string;
clientSecret: string;
isActive: boolean;
orgSlug: string;
organizationId: string;
manageGroupMemberships: boolean;
jwtSignatureAlgorithm: OIDCJWTSignatureAlgorithm;
}> &

View File

@ -148,10 +148,18 @@ export const samlConfigServiceFactory = ({
let samlConfig: TSamlConfigs | undefined;
if (dto.type === "org") {
samlConfig = await samlConfigDAL.findOne({ orgId: dto.orgId });
if (!samlConfig) return;
if (!samlConfig) {
throw new NotFoundError({
message: `SAML configuration for organization with ID '${dto.orgId}' not found`
});
}
} else if (dto.type === "orgSlug") {
const org = await orgDAL.findOne({ slug: dto.orgSlug });
if (!org) return;
if (!org) {
throw new NotFoundError({
message: `Organization with slug '${dto.orgSlug}' not found`
});
}
samlConfig = await samlConfigDAL.findOne({ orgId: org.id });
} else if (dto.type === "ssoId") {
// TODO:

View File

@ -61,20 +61,17 @@ export type TSamlLoginDTO = {
export type TSamlConfigServiceFactory = {
createSamlCfg: (arg: TCreateSamlCfgDTO) => Promise<TSamlConfigs>;
updateSamlCfg: (arg: TUpdateSamlCfgDTO) => Promise<TSamlConfigs>;
getSaml: (arg: TGetSamlCfgDTO) => Promise<
| {
id: string;
organization: string;
orgId: string;
authProvider: string;
isActive: boolean;
entryPoint: string;
issuer: string;
cert: string;
lastUsed: Date | null | undefined;
}
| undefined
>;
getSaml: (arg: TGetSamlCfgDTO) => Promise<{
id: string;
organization: string;
orgId: string;
authProvider: string;
isActive: boolean;
entryPoint: string;
issuer: string;
cert: string;
lastUsed: Date | null | undefined;
}>;
samlLogin: (arg: TSamlLoginDTO) => Promise<{
isUserCompleted: boolean;
providerAuthToken: string;

View File

@ -66,7 +66,10 @@ export enum ApiDocsTags {
KmsKeys = "KMS Keys",
KmsEncryption = "KMS Encryption",
KmsSigning = "KMS Signing",
SecretScanning = "Secret Scanning"
SecretScanning = "Secret Scanning",
OidcSso = "OIDC SSO",
SamlSso = "SAML SSO",
LdapSso = "LDAP SSO"
}
export const GROUPS = {
@ -2662,3 +2665,113 @@ export const SecretScanningConfigs = {
content: "The contents of the Secret Scanning Configuration file."
}
};
export const OidcSSo = {
GET_CONFIG: {
organizationId: "The ID of the organization to get the OIDC config for."
},
UPDATE_CONFIG: {
organizationId: "The ID of the organization to update the OIDC config for.",
allowedEmailDomains:
"A list of allowed email domains that users can use to authenticate with. This field is comma separated. Example: 'example.com,acme.com'",
discoveryURL: "The URL of the OIDC discovery endpoint.",
configurationType: "The configuration type to use for the OIDC configuration.",
issuer:
"The issuer for the OIDC configuration. This is only supported when the OIDC configuration type is set to 'custom'.",
authorizationEndpoint:
"The endpoint to use for OIDC authorization. This is only supported when the OIDC configuration type is set to 'custom'.",
jwksUri: "The URL of the OIDC JWKS endpoint.",
tokenEndpoint: "The token endpoint to use for OIDC token exchange.",
userinfoEndpoint: "The userinfo endpoint to get user information from the OIDC provider.",
clientId: "The client ID to use for OIDC authentication.",
clientSecret: "The client secret to use for OIDC authentication.",
isActive: "Whether to enable or disable this OIDC configuration.",
manageGroupMemberships:
"Whether to manage group memberships for the OIDC configuration. If enabled, users will automatically be assigned groups when they sign in, based on which groups they are a member of in the OIDC provider.",
jwtSignatureAlgorithm: "The algorithm to use for JWT signature verification."
},
CREATE_CONFIG: {
organizationId: "The ID of the organization to create the OIDC config for.",
allowedEmailDomains:
"A list of allowed email domains that users can use to authenticate with. This field is comma separated.",
discoveryURL: "The URL of the OIDC discovery endpoint.",
configurationType: "The configuration type to use for the OIDC configuration.",
issuer:
"The issuer for the OIDC configuration. This is only supported when the OIDC configuration type is set to 'custom'.",
authorizationEndpoint:
"The authorization endpoint to use for OIDC authorization. This is only supported when the OIDC configuration type is set to 'custom'.",
jwksUri: "The URL of the OIDC JWKS endpoint.",
tokenEndpoint: "The token endpoint to use for OIDC token exchange.",
userinfoEndpoint: "The userinfo endpoint to get user information from the OIDC provider.",
clientId: "The client ID to use for OIDC authentication.",
clientSecret: "The client secret to use for OIDC authentication.",
isActive: "Whether to enable or disable this OIDC configuration.",
manageGroupMemberships:
"Whether to manage group memberships for the OIDC configuration. If enabled, users will automatically be assigned groups when they sign in, based on which groups they are a member of in the OIDC provider.",
jwtSignatureAlgorithm: "The algorithm to use for JWT signature verification."
}
};
export const SamlSso = {
GET_CONFIG: {
organizationId: "The ID of the organization to get the SAML config for."
},
UPDATE_CONFIG: {
organizationId: "The ID of the organization to update the SAML config for.",
authProvider: "Authentication provider to use for SAML authentication.",
isActive: "Whether to enable or disable this SAML configuration.",
entryPoint:
"The entry point for the SAML authentication. This is the URL that the user will be redirected to after they have authenticated with the SAML provider.",
issuer: "The SAML provider issuer URL or entity ID.",
cert: "The certificate to use for SAML authentication."
},
CREATE_CONFIG: {
organizationId: "The ID of the organization to create the SAML config for.",
authProvider: "Authentication provider to use for SAML authentication.",
isActive: "Whether to enable or disable this SAML configuration.",
entryPoint:
"The entry point for the SAML authentication. This is the URL that the user will be redirected to after they have authenticated with the SAML provider.",
issuer: "The SAML provider issuer URL or entity ID.",
cert: "The certificate to use for SAML authentication."
}
};
export const LdapSso = {
GET_CONFIG: {
organizationId: "The ID of the organization to get the LDAP config for."
},
CREATE_CONFIG: {
organizationId: "The ID of the organization to create the LDAP config for.",
isActive: "Whether to enable or disable this LDAP configuration.",
url: "The LDAP server to connect to such as `ldap://ldap.your-org.com`, `ldaps://ldap.myorg.com:636` (for connection over SSL/TLS), etc.",
bindDN:
"The distinguished name of the object to bind when performing the user search such as `cn=infisical,ou=Users,dc=acme,dc=com`",
bindPass: "The password to use along with Bind DN when performing the user search.",
searchBase: "The base DN to use for the user search such as `ou=Users,dc=acme,dc=com`",
uniqueUserAttribute:
"The attribute to use as the unique identifier of LDAP users such as `sAMAccountName`, `cn`, `uid`, `objectGUID`. If left blank, defaults to uidNumber",
searchFilter:
"The template used to construct the LDAP user search filter such as `(uid={{username}})` uses literal `{{username}}` to have the given username used in the search. The default is `(uid={{username}})` which is compatible with several common directory schemas.",
groupSearchBase: "LDAP search base to use for group membership search such as `ou=Groups,dc=acme,dc=com`",
groupSearchFilter:
"The template used when constructing the group membership query such as `(&(objectClass=posixGroup)(memberUid={{.Username}}))`. The template can access the following context variables: `[UserDN, UserName]`. The default is `(|(memberUid={{.Username}})(member={{.UserDN}})(uniqueMember={{.UserDN}}))` which is compatible with several common directory schemas.",
caCert: "The CA certificate to use when verifying the LDAP server certificate."
},
UPDATE_CONFIG: {
organizationId: "The ID of the organization to update the LDAP config for.",
isActive: "Whether to enable or disable this LDAP configuration.",
url: "The LDAP server to connect to such as `ldap://ldap.your-org.com`, `ldaps://ldap.myorg.com:636` (for connection over SSL/TLS), etc.",
bindDN:
"The distinguished name of object to bind when performing the user search such as `cn=infisical,ou=Users,dc=acme,dc=com`",
bindPass: "The password to use along with Bind DN when performing the user search.",
uniqueUserAttribute:
"The attribute to use as the unique identifier of LDAP users such as `sAMAccountName`, `cn`, `uid`, `objectGUID`. If left blank, defaults to uidNumber",
searchFilter:
"The template used to construct the LDAP user search filter such as `(uid={{username}})` uses literal `{{username}}` to have the given username used in the search. The default is `(uid={{username}})` which is compatible with several common directory schemas.",
searchBase: "The base DN to use for the user search such as `ou=Users,dc=acme,dc=com`",
groupSearchBase: "LDAP search base to use for group membership search such as `ou=Groups,dc=acme,dc=com`",
groupSearchFilter:
"The template used when constructing the group membership query such as `(&(objectClass=posixGroup)(memberUid={{.Username}}))`. The template can access the following context variables: `[UserDN, UserName]`. The default is `(|(memberUid={{.Username}})(member={{.UserDN}})(uniqueMember={{.UserDN}}))` which is compatible with several common directory schemas.",
caCert: "The CA certificate to use when verifying the LDAP server certificate."
}
};

View File

@ -1911,6 +1911,7 @@ export const registerRoutes = async (
await hsmService.startService();
await telemetryQueue.startTelemetryCheck();
await telemetryQueue.startAggregatedEventsJob();
await dailyResourceCleanUp.startCleanUp();
await dailyExpiringPkiItemAlert.startSendingAlerts();
await pkiSubscriberQueue.startDailyAutoRenewalJob();

View File

@ -122,8 +122,8 @@ export const orgMembershipDALFactory = (db: TDbClient) => {
.orWhere((qb) => {
// lastInvitedAt is older than 1 week ago AND createdAt is younger than 1 month ago
void qb
.where(`${TableName.OrgMembership}.lastInvitedAt`, "<", oneMonthAgo)
.where(`${TableName.OrgMembership}.createdAt`, ">", oneWeekAgo);
.where(`${TableName.OrgMembership}.lastInvitedAt`, "<", oneWeekAgo)
.where(`${TableName.OrgMembership}.createdAt`, ">", oneMonthAgo);
});
return memberships;
@ -135,9 +135,22 @@ export const orgMembershipDALFactory = (db: TDbClient) => {
}
};
const updateLastInvitedAtByIds = async (membershipIds: string[]) => {
try {
if (membershipIds.length === 0) return;
await db(TableName.OrgMembership).whereIn("id", membershipIds).update({ lastInvitedAt: new Date() });
} catch (error) {
throw new DatabaseError({
error,
name: "Update last invited at by ids"
});
}
};
return {
...orgMembershipOrm,
findOrgMembershipById,
findRecentInvitedMemberships
findRecentInvitedMemberships,
updateLastInvitedAtByIds
};
};

View File

@ -109,7 +109,12 @@ type TOrgServiceFactoryDep = {
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "delete" | "insertMany" | "findLatestProjectKey" | "create">;
orgMembershipDAL: Pick<
TOrgMembershipDALFactory,
"findOrgMembershipById" | "findOne" | "findById" | "findRecentInvitedMemberships" | "updateById"
| "findOrgMembershipById"
| "findOne"
| "findById"
| "findRecentInvitedMemberships"
| "updateById"
| "updateLastInvitedAtByIds"
>;
incidentContactDAL: TIncidentContactsDALFactory;
samlConfigDAL: Pick<TSamlConfigDALFactory, "findOne">;
@ -763,6 +768,10 @@ export const orgServiceFactory = ({
}
});
await orgMembershipDAL.updateById(inviteeOrgMembership.id, {
lastInvitedAt: new Date()
});
return { signupToken: undefined };
};
@ -1433,6 +1442,7 @@ export const orgServiceFactory = ({
const appCfg = getConfig();
const orgCache: Record<string, { name: string; id: string } | undefined> = {};
const notifiedUsers: string[] = [];
await Promise.all(
invitedUsers.map(async (invitedUser) => {
@ -1463,13 +1473,12 @@ export const orgServiceFactory = ({
callback_url: `${appCfg.SITE_URL}/signupinvite`
}
});
notifiedUsers.push(invitedUser.id);
}
await orgMembershipDAL.updateById(invitedUser.id, {
lastInvitedAt: new Date()
});
})
);
await orgMembershipDAL.updateLastInvitedAtByIds(notifiedUsers);
};
return {

View File

@ -71,6 +71,15 @@ export const telemetryQueueServiceFactory = ({
QueueName.TelemetryInstanceStats // just a job id
);
if (postHog) {
await queueService.queue(QueueName.TelemetryInstanceStats, QueueJobs.TelemetryInstanceStats, undefined, {
jobId: QueueName.TelemetryInstanceStats,
repeat: { pattern: "0 0 * * *", utc: true }
});
}
};
const startAggregatedEventsJob = async () => {
// clear previous aggregated events job
await queueService.stopRepeatableJob(
QueueName.TelemetryAggregatedEvents,
@ -80,11 +89,6 @@ export const telemetryQueueServiceFactory = ({
);
if (postHog) {
await queueService.queue(QueueName.TelemetryInstanceStats, QueueJobs.TelemetryInstanceStats, undefined, {
jobId: QueueName.TelemetryInstanceStats,
repeat: { pattern: "0 0 * * *", utc: true }
});
// Start aggregated events job (runs every five minutes)
await queueService.queue(QueueName.TelemetryAggregatedEvents, QueueJobs.TelemetryAggregatedEvents, undefined, {
jobId: QueueName.TelemetryAggregatedEvents,
@ -102,6 +106,7 @@ export const telemetryQueueServiceFactory = ({
});
return {
startTelemetryCheck
startTelemetryCheck,
startAggregatedEventsJob
};
};

View File

@ -14,7 +14,7 @@ export const TELEMETRY_SECRET_PROCESSED_KEY = "telemetry-secret-processed";
export const TELEMETRY_SECRET_OPERATIONS_KEY = "telemetry-secret-operations";
export const POSTHOG_AGGREGATED_EVENTS = [PostHogEventTypes.SecretPulled];
const TELEMETRY_AGGREGATED_KEY_EXP = 900; // 15mins
const TELEMETRY_AGGREGATED_KEY_EXP = 600; // 10mins
// Bucket configuration
const TELEMETRY_BUCKET_COUNT = 30;
@ -102,13 +102,6 @@ To opt into telemetry, you can set "TELEMETRY_ENABLED=true" within the environme
const instanceType = licenseService.getInstanceType();
// capture posthog only when its cloud or signup event happens in self-hosted
if (instanceType === InstanceType.Cloud || event.event === PostHogEventTypes.UserSignedUp) {
if (event.organizationId) {
try {
postHog.groupIdentify({ groupType: "organization", groupKey: event.organizationId });
} catch (error) {
logger.error(error, "Failed to identify PostHog organization");
}
}
if (POSTHOG_AGGREGATED_EVENTS.includes(event.event)) {
const eventKey = createTelemetryEventKey(event.event, event.distinctId);
await keyStore.setItemWithExpiry(
@ -122,6 +115,13 @@ To opt into telemetry, you can set "TELEMETRY_ENABLED=true" within the environme
})
);
} else {
if (event.organizationId) {
try {
postHog.groupIdentify({ groupType: "organization", groupKey: event.organizationId });
} catch (error) {
logger.error(error, "Failed to identify PostHog organization");
}
}
postHog.capture({
event: event.event,
distinctId: event.distinctId,

View File

@ -35,6 +35,7 @@ const (
GitHubPlatform
GitLabPlatform
AzureDevOpsPlatform
BitBucketPlatform
// TODO: Add others.
)
@ -45,6 +46,7 @@ func (p Platform) String() string {
"github",
"gitlab",
"azuredevops",
"bitbucket",
}[p]
}
@ -60,6 +62,8 @@ func PlatformFromString(s string) (Platform, error) {
return GitLabPlatform, nil
case "azuredevops":
return AzureDevOpsPlatform, nil
case "bitbucket":
return BitBucketPlatform, nil
default:
return UnknownPlatform, fmt.Errorf("invalid scm platform value: %s", s)
}

View File

@ -208,6 +208,8 @@ func platformFromHost(u *url.URL) scm.Platform {
return scm.GitLabPlatform
case "dev.azure.com", "visualstudio.com":
return scm.AzureDevOpsPlatform
case "bitbucket.org":
return scm.BitBucketPlatform
default:
return scm.UnknownPlatform
}

View File

@ -112,6 +112,15 @@ func createScmLink(scmPlatform scm.Platform, remoteUrl string, finding report.Fi
// This is a bit dirty, but Azure DevOps does not highlight the line when the lineStartColumn and lineEndColumn are not provided
link += "&lineStartColumn=1&lineEndColumn=10000000&type=2&lineStyle=plain&_a=files"
return link
case scm.BitBucketPlatform:
link := fmt.Sprintf("%s/src/%s/%s", remoteUrl, finding.Commit, filePath)
if finding.StartLine != 0 {
link += fmt.Sprintf("#lines-%d", finding.StartLine)
}
if finding.EndLine != finding.StartLine {
link += fmt.Sprintf(":%d", finding.EndLine)
}
return link
default:
// This should never happen.
return ""

View File

@ -337,9 +337,7 @@ var scanCmd = &cobra.Command{
if gitCmd, err = sources.NewGitLogCmd(source, logOpts); err != nil {
logging.Fatal().Err(err).Msg("could not create Git cmd")
}
if scmPlatform, err = scm.PlatformFromString("github"); err != nil {
logging.Fatal().Err(err).Send()
}
scmPlatform = scm.UnknownPlatform
remote = detect.NewRemoteInfo(scmPlatform, source)
if findings, err = detector.DetectGit(gitCmd, remote); err != nil {

View File

@ -0,0 +1,4 @@
---
title: "Create LDAP SSO Config"
openapi: "POST /api/v1/ldap/config"
---

View File

@ -0,0 +1,4 @@
---
title: "Get LDAP SSO Config"
openapi: "GET /api/v1/ldap/config"
---

View File

@ -0,0 +1,4 @@
---
title: "Update LDAP SSO Config"
openapi: "PATCH /api/v1/ldap/config"
---

View File

@ -0,0 +1,4 @@
---
title: "Create OIDC Config"
openapi: "POST /api/v1/sso/oidc/config"
---

View File

@ -0,0 +1,4 @@
---
title: "Get OIDC Config"
openapi: "GET /api/v1/sso/oidc/config"
---

View File

@ -0,0 +1,4 @@
---
title: "Update OIDC Config"
openapi: "PATCH /api/v1/sso/oidc/config"
---

View File

@ -0,0 +1,4 @@
---
title: "Create SAML SSO Config"
openapi: "POST /api/v1/sso/config"
---

View File

@ -0,0 +1,4 @@
---
title: "Get SAML SSO Config"
openapi: "GET /api/v1/sso/config"
---

View File

@ -0,0 +1,4 @@
---
title: "Update SAML SSO Config"
openapi: "PATCH /api/v1/sso/config"
---

View File

@ -851,6 +851,30 @@
{
"group": "Organizations",
"pages": [
{
"group": "OIDC SSO",
"pages": [
"api-reference/endpoints/organizations/oidc-sso/get-oidc-config",
"api-reference/endpoints/organizations/oidc-sso/update-oidc-config",
"api-reference/endpoints/organizations/oidc-sso/create-oidc-config"
]
},
{
"group": "LDAP SSO",
"pages": [
"api-reference/endpoints/organizations/ldap-sso/get-ldap-config",
"api-reference/endpoints/organizations/ldap-sso/update-ldap-config",
"api-reference/endpoints/organizations/ldap-sso/create-ldap-config"
]
},
{
"group": "SAML SSO",
"pages": [
"api-reference/endpoints/organizations/saml-sso/get-saml-config",
"api-reference/endpoints/organizations/saml-sso/update-saml-config",
"api-reference/endpoints/organizations/saml-sso/create-saml-config"
]
},
"api-reference/endpoints/organizations/memberships",
"api-reference/endpoints/organizations/update-membership",
"api-reference/endpoints/organizations/delete-membership",

View File

@ -33,7 +33,7 @@ export const ContentLoader = ({ text, frequency = 2000, className }: Props) => {
className
)}
>
<Lottie icon="infisical_loading" className="h-32 w-32" />
<Lottie isAutoPlay icon="infisical_loading" className="h-32 w-32" />
{text && isTextArray && (
<AnimatePresence mode="wait">
<motion.div

View File

@ -43,23 +43,25 @@ export const Tooltip = ({
onOpenChange={onOpenChange}
>
<TooltipPrimitive.Trigger asChild={asChild}>{children}</TooltipPrimitive.Trigger>
<TooltipPrimitive.Content
side={position}
align="center"
sideOffset={5}
{...props}
className={twMerge(
"z-50 max-w-[15rem] select-none border border-mineshaft-600 bg-mineshaft-800 font-light text-bunker-200 shadow-md data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade",
isDisabled && "!hidden",
center && "text-center",
size === "sm" && "rounded-sm px-2 py-1 text-xs",
size === "md" && "rounded-md px-4 py-2 text-sm",
className
)}
>
{content}
<TooltipPrimitive.Arrow width={11} height={5} className="fill-mineshaft-600" />
</TooltipPrimitive.Content>
<TooltipPrimitive.Portal>
<TooltipPrimitive.Content
side={position}
align="center"
sideOffset={5}
{...props}
className={twMerge(
"z-50 max-w-[15rem] select-none border border-mineshaft-600 bg-mineshaft-800 font-light text-bunker-200 shadow-md data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade",
isDisabled && "!hidden",
center && "text-center",
size === "sm" && "rounded-sm px-2 py-1 text-xs",
size === "md" && "rounded-md px-4 py-2 text-sm",
className
)}
>
{content}
<TooltipPrimitive.Arrow width={11} height={5} className="fill-mineshaft-600" />
</TooltipPrimitive.Content>
</TooltipPrimitive.Portal>
</TooltipPrimitive.Root>
) : (
// eslint-disable-next-line react/jsx-no-useless-fragment

View File

@ -21,7 +21,7 @@ export const useUpdateOIDCConfig = () => {
clientId,
clientSecret,
isActive,
orgSlug,
organizationId,
manageGroupMemberships,
jwtSignatureAlgorithm
}: {
@ -36,7 +36,7 @@ export const useUpdateOIDCConfig = () => {
clientSecret?: string;
isActive?: boolean;
configurationType?: string;
orgSlug: string;
organizationId: string;
manageGroupMemberships?: boolean;
jwtSignatureAlgorithm?: OIDCJWTSignatureAlgorithm;
}) => {
@ -50,7 +50,7 @@ export const useUpdateOIDCConfig = () => {
tokenEndpoint,
userinfoEndpoint,
clientId,
orgSlug,
organizationId,
clientSecret,
isActive,
manageGroupMemberships,
@ -60,7 +60,7 @@ export const useUpdateOIDCConfig = () => {
return data;
},
onSuccess(_, dto) {
queryClient.invalidateQueries({ queryKey: oidcConfigKeys.getOIDCConfig(dto.orgSlug) });
queryClient.invalidateQueries({ queryKey: oidcConfigKeys.getOIDCConfig(dto.organizationId) });
queryClient.invalidateQueries({ queryKey: organizationKeys.getUserOrganizations });
}
});
@ -81,7 +81,7 @@ export const useCreateOIDCConfig = () => {
clientId,
clientSecret,
isActive,
orgSlug,
organizationId,
manageGroupMemberships,
jwtSignatureAlgorithm
}: {
@ -95,7 +95,7 @@ export const useCreateOIDCConfig = () => {
clientId: string;
clientSecret: string;
isActive: boolean;
orgSlug: string;
organizationId: string;
allowedEmailDomains?: string;
manageGroupMemberships?: boolean;
jwtSignatureAlgorithm?: OIDCJWTSignatureAlgorithm;
@ -112,7 +112,7 @@ export const useCreateOIDCConfig = () => {
clientId,
clientSecret,
isActive,
orgSlug,
organizationId,
manageGroupMemberships,
jwtSignatureAlgorithm
});
@ -120,7 +120,7 @@ export const useCreateOIDCConfig = () => {
return data;
},
onSuccess(_, dto) {
queryClient.invalidateQueries({ queryKey: oidcConfigKeys.getOIDCConfig(dto.orgSlug) });
queryClient.invalidateQueries({ queryKey: oidcConfigKeys.getOIDCConfig(dto.organizationId) });
}
});
};

View File

@ -5,18 +5,18 @@ import { apiRequest } from "@app/config/request";
import { OIDCConfigData } from "./types";
export const oidcConfigKeys = {
getOIDCConfig: (orgSlug: string) => [{ orgSlug }, "organization-oidc"] as const,
getOIDCConfig: (orgId: string) => [{ orgId }, "organization-oidc"] as const,
getOIDCManageGroupMembershipsEnabled: (orgId: string) =>
["oidc-manage-group-memberships", orgId] as const
};
export const useGetOIDCConfig = (orgSlug: string) => {
export const useGetOIDCConfig = (orgId: string) => {
return useQuery({
queryKey: oidcConfigKeys.getOIDCConfig(orgSlug),
queryKey: oidcConfigKeys.getOIDCConfig(orgId),
queryFn: async () => {
try {
const { data } = await apiRequest.get<OIDCConfigData>(
`/api/v1/sso/oidc/config?orgSlug=${orgSlug}`
`/api/v1/sso/oidc/config?organizationId=${orgId}`
);
return data;

View File

@ -113,7 +113,7 @@ export const ProjectSelect = () => {
<div>
<FontAwesomeIcon icon={faCube} className="text-xs" />
</div>
<Tooltip content={currentWorkspace.name}>
<Tooltip content={currentWorkspace.name} className="max-w-96">
<div className="max-w-32 overflow-hidden text-ellipsis whitespace-nowrap">
{currentWorkspace?.name}
</div>
@ -176,7 +176,7 @@ export const ProjectSelect = () => {
>
<div className="flex items-center">
<div className="flex flex-1 items-center justify-between overflow-hidden">
<Tooltip content={workspace.name}>
<Tooltip side="right" className="break-words" content={workspace.name}>
<div className="max-w-40 overflow-hidden truncate whitespace-nowrap">
{workspace.name}
</div>

View File

@ -63,7 +63,7 @@ const router = createRouter({
context: { serverConfig: null, queryClient },
defaultPendingComponent: () => (
<div className="flex h-screen w-screen items-center justify-center bg-bunker-800">
<Lottie icon="infisical_loading" className="h-32 w-32" />
<Lottie isAutoPlay icon="infisical_loading" className="h-32 w-32" />
</div>
),
defaultNotFoundComponent: NotFoundPage,

View File

@ -105,7 +105,7 @@ export const OIDCModal = ({ popUp, handlePopUpClose, handlePopUpToggle, hideDele
const { mutateAsync: updateMutateAsync, isPending: updateIsLoading } = useUpdateOIDCConfig();
const [isDeletePopupOpen, setIsDeletePopupOpen] = useToggle(false);
const { data } = useGetOIDCConfig(currentOrg?.slug ?? "");
const { data } = useGetOIDCConfig(currentOrg?.id ?? "");
const { control, handleSubmit, reset, setValue, watch } = useForm<OIDCFormData>({
resolver: zodResolver(schema),
@ -134,7 +134,7 @@ export const OIDCModal = ({ popUp, handlePopUpClose, handlePopUpToggle, hideDele
clientId: "",
clientSecret: "",
isActive: false,
orgSlug: currentOrg.slug
organizationId: currentOrg.id
});
createNotification({
@ -196,7 +196,7 @@ export const OIDCModal = ({ popUp, handlePopUpClose, handlePopUpToggle, hideDele
clientId,
clientSecret,
isActive: true,
orgSlug: currentOrg.slug,
organizationId: currentOrg.id,
jwtSignatureAlgorithm
});
} else {
@ -212,7 +212,7 @@ export const OIDCModal = ({ popUp, handlePopUpClose, handlePopUpToggle, hideDele
clientId,
clientSecret,
isActive: true,
orgSlug: currentOrg.slug,
organizationId: currentOrg.id,
jwtSignatureAlgorithm
});
}

View File

@ -21,7 +21,7 @@ export const OrgOIDCSection = (): JSX.Element => {
const { currentOrg } = useOrganization();
const { subscription } = useSubscription();
const { data, isPending } = useGetOIDCConfig(currentOrg?.slug ?? "");
const { data, isPending } = useGetOIDCConfig(currentOrg?.id ?? "");
const { mutateAsync } = useUpdateOIDCConfig();
const { mutateAsync: updateOrg } = useUpdateOrg();
@ -41,7 +41,7 @@ export const OrgOIDCSection = (): JSX.Element => {
}
await mutateAsync({
orgSlug: currentOrg?.slug,
organizationId: currentOrg?.id,
isActive: value
});
@ -114,7 +114,7 @@ export const OrgOIDCSection = (): JSX.Element => {
}
await mutateAsync({
orgSlug: currentOrg?.slug,
organizationId: currentOrg?.id,
manageGroupMemberships: value
});

View File

@ -38,7 +38,7 @@ export const OrgSsoTab = withPermission(
const { subscription } = useSubscription();
const { data: oidcConfig, isPending: isLoadingOidcConfig } = useGetOIDCConfig(
currentOrg?.slug ?? ""
currentOrg?.id ?? ""
);
const { data: samlConfig, isPending: isLoadingSamlConfig } = useGetSSOConfig(
currentOrg?.id ?? ""

View File

@ -891,7 +891,7 @@ export const OverviewPage = () => {
if (isProjectV3 && visibleEnvs.length > 0 && isOverviewLoading) {
return (
<div className="container mx-auto flex h-screen w-full items-center justify-center px-8 text-mineshaft-50 dark:[color-scheme:dark]">
<Lottie icon="infisical_loading" className="h-32 w-32" />
<Lottie isAutoPlay icon="infisical_loading" className="h-32 w-32" />
</div>
);
}

View File

@ -253,7 +253,7 @@ export const SecretDropzone = ({
>
{isLoading ? (
<div className="mb-16 flex items-center justify-center pt-16">
<Lottie icon="infisical_loading" className="h-32 w-32" />
<Lottie isAutoPlay icon="infisical_loading" className="h-32 w-32" />
</div>
) : (
<div className="flex flex-col items-center justify-center space-y-2">