From 69c50af14ebbf65c25d12d5cb6b491f4b94cf490 Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Mon, 29 Apr 2024 11:53:28 -0700 Subject: [PATCH] Move trust saml/ldap emails to server config --- ...0426162819_user-alias-optional-username.ts | 31 +++++++-- backend/src/db/schemas/super-admin.ts | 4 +- .../ldap-config/ldap-config-service.ts | 4 +- .../saml-config/saml-config-service.ts | 4 +- backend/src/ee/services/scim/scim-service.ts | 6 +- backend/src/lib/config/env.ts | 3 - backend/src/server/routes/v1/admin-router.ts | 4 +- docs/self-hosting/configuration/envars.mdx | 10 --- frontend/src/hooks/api/admin/types.ts | 2 + .../admin/DashboardPage/DashboardPage.tsx | 68 ++++++++++++++++--- 10 files changed, 102 insertions(+), 34 deletions(-) diff --git a/backend/src/db/migrations/20240426162819_user-alias-optional-username.ts b/backend/src/db/migrations/20240426162819_user-alias-optional-username.ts index 380660149..8e7fd8b8b 100644 --- a/backend/src/db/migrations/20240426162819_user-alias-optional-username.ts +++ b/backend/src/db/migrations/20240426162819_user-alias-optional-username.ts @@ -3,9 +3,32 @@ import { Knex } from "knex"; import { TableName } from "../schemas"; export async function up(knex: Knex): Promise { - await knex.schema.alterTable(TableName.UserAliases, (t) => { - t.string("username").nullable().alter(); - }); + const isUserAliasTablePresent = await knex.schema.hasTable(TableName.SuperAdmin); + if (isUserAliasTablePresent) { + await knex.schema.alterTable(TableName.UserAliases, (t) => { + t.string("username").nullable().alter(); + }); + } + + const isSuperAdminTablePresent = await knex.schema.hasTable(TableName.SuperAdmin); + if (isSuperAdminTablePresent) { + await knex.schema.alterTable(TableName.SuperAdmin, (t) => { + t.boolean("trustSamlEmails").defaultTo(false); + t.boolean("trustLdapEmails").defaultTo(false); + }); + } } -export async function down(): Promise {} +export async function down(knex: Knex): Promise { + if (await knex.schema.hasColumn(TableName.SuperAdmin, "trustSamlEmails")) { + await knex.schema.alterTable(TableName.SuperAdmin, (t) => { + t.dropColumn("trustSamlEmails"); + }); + } + + if (await knex.schema.hasColumn(TableName.SuperAdmin, "trustLdapEmails")) { + await knex.schema.alterTable(TableName.SuperAdmin, (t) => { + t.dropColumn("trustLdapEmails"); + }); + } +} diff --git a/backend/src/db/schemas/super-admin.ts b/backend/src/db/schemas/super-admin.ts index 958fed0ab..417d4e05e 100644 --- a/backend/src/db/schemas/super-admin.ts +++ b/backend/src/db/schemas/super-admin.ts @@ -14,7 +14,9 @@ export const SuperAdminSchema = z.object({ createdAt: z.date(), updatedAt: z.date(), allowedSignUpDomain: z.string().nullable().optional(), - instanceId: z.string().uuid().default("00000000-0000-0000-0000-000000000000") + instanceId: z.string().uuid().default("00000000-0000-0000-0000-000000000000"), + trustSamlEmails: z.boolean().default(false).nullable().optional(), + trustLdapEmails: z.boolean().default(false).nullable().optional() }); export type TSuperAdmin = z.infer; diff --git a/backend/src/ee/services/ldap-config/ldap-config-service.ts b/backend/src/ee/services/ldap-config/ldap-config-service.ts index f712a6ea4..d799b1754 100644 --- a/backend/src/ee/services/ldap-config/ldap-config-service.ts +++ b/backend/src/ee/services/ldap-config/ldap-config-service.ts @@ -28,6 +28,7 @@ import { TOrgDALFactory } from "@app/services/org/org-dal"; import { TProjectDALFactory } from "@app/services/project/project-dal"; import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal"; import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal"; +import { getServerCfg } from "@app/services/super-admin/super-admin-service"; import { TUserDALFactory } from "@app/services/user/user-dal"; import { normalizeUsername } from "@app/services/user/user-fns"; import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal"; @@ -392,6 +393,7 @@ export const ldapConfigServiceFactory = ({ relayState }: TLdapLoginDTO) => { const appCfg = getConfig(); + const serverCfg = await getServerCfg(); let userAlias = await userAliasDAL.findOne({ externalId, orgId, @@ -437,7 +439,7 @@ export const ldapConfigServiceFactory = ({ { username: uniqueUsername, email: emails[0], - isEmailVerified: appCfg.TRUST_LDAP_EMAILS, + isEmailVerified: serverCfg.trustLdapEmails, firstName, lastName, authMethods: [], diff --git a/backend/src/ee/services/saml-config/saml-config-service.ts b/backend/src/ee/services/saml-config/saml-config-service.ts index ff296103b..9719b434b 100644 --- a/backend/src/ee/services/saml-config/saml-config-service.ts +++ b/backend/src/ee/services/saml-config/saml-config-service.ts @@ -24,6 +24,7 @@ import { AuthTokenType } from "@app/services/auth/auth-type"; import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal"; import { TOrgDALFactory } from "@app/services/org/org-dal"; import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal"; +import { getServerCfg } from "@app/services/super-admin/super-admin-service"; import { TUserDALFactory } from "@app/services/user/user-dal"; import { normalizeUsername } from "@app/services/user/user-fns"; import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal"; @@ -323,6 +324,7 @@ export const samlConfigServiceFactory = ({ relayState }: TSamlLoginDTO) => { const appCfg = getConfig(); + const serverCfg = await getServerCfg(); const userAlias = await userAliasDAL.findOne({ externalId, orgId, @@ -374,7 +376,7 @@ export const samlConfigServiceFactory = ({ { username: uniqueUsername, email, - isEmailVerified: appCfg.TRUST_SAML_EMAILS, + isEmailVerified: serverCfg.trustSamlEmails, firstName, lastName, authMethods: [], diff --git a/backend/src/ee/services/scim/scim-service.ts b/backend/src/ee/services/scim/scim-service.ts index db0413fde..c9cc6e1ed 100644 --- a/backend/src/ee/services/scim/scim-service.ts +++ b/backend/src/ee/services/scim/scim-service.ts @@ -21,6 +21,7 @@ import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal"; import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal"; import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service"; +import { getServerCfg } from "@app/services/super-admin/super-admin-service"; import { TUserDALFactory } from "@app/services/user/user-dal"; import { normalizeUsername } from "@app/services/user/user-fns"; import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal"; @@ -81,8 +82,6 @@ type TScimServiceFactoryDep = { export type TScimServiceFactory = ReturnType; -// TODO: finish updating all userId refs to orgMembershipId - export const scimServiceFactory = ({ licenseService, scimDAL, @@ -279,6 +278,7 @@ export const scimServiceFactory = ({ }); const appCfg = getConfig(); + const serverCfg = await getServerCfg(); const userAlias = await userAliasDAL.findOne({ externalId: username, @@ -325,7 +325,7 @@ export const scimServiceFactory = ({ { username: uniqueUsername, email, - isEmailVerified: appCfg.TRUST_SAML_EMAILS, + isEmailVerified: serverCfg.trustSamlEmails, firstName, lastName, authMethods: [], diff --git a/backend/src/lib/config/env.ts b/backend/src/lib/config/env.ts index 8a365f284..4d3d55ffd 100644 --- a/backend/src/lib/config/env.ts +++ b/backend/src/lib/config/env.ts @@ -98,9 +98,6 @@ const envSchema = z CLIENT_ID_GITLAB: zpStr(z.string().optional()), CLIENT_SECRET_GITLAB: zpStr(z.string().optional()), URL_GITLAB_URL: zpStr(z.string().optional().default(GITLAB_URL)), - // email verification - TRUST_SAML_EMAILS: zodStrBool.default("false"), - TRUST_LDAP_EMAILS: zodStrBool.default("false"), // SECRET-SCANNING SECRET_SCANNING_WEBHOOK_PROXY: zpStr(z.string().optional()), SECRET_SCANNING_WEBHOOK_SECRET: zpStr(z.string().optional()), diff --git a/backend/src/server/routes/v1/admin-router.ts b/backend/src/server/routes/v1/admin-router.ts index e70822128..4882411d8 100644 --- a/backend/src/server/routes/v1/admin-router.ts +++ b/backend/src/server/routes/v1/admin-router.ts @@ -42,7 +42,9 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => { schema: { body: z.object({ allowSignUp: z.boolean().optional(), - allowedSignUpDomain: z.string().optional().nullable() + allowedSignUpDomain: z.string().optional().nullable(), + trustSamlEmails: z.boolean().optional(), + trustLdapEmails: z.boolean().optional() }), response: { 200: z.object({ diff --git a/docs/self-hosting/configuration/envars.mdx b/docs/self-hosting/configuration/envars.mdx index e9f4431a4..5233ae910 100644 --- a/docs/self-hosting/configuration/envars.mdx +++ b/docs/self-hosting/configuration/envars.mdx @@ -369,16 +369,6 @@ To login into Infisical with OAuth providers such as Google, configure the assoc information. - - Whether or not to trust emails from external SAML identity providers. If set - to `false` then users will be prompted to verify their email address upon - first login. - - - Whether or not to trust emails from external LDAP servers. If set to `false` - then users will be prompted to verify their email address upon first login. - - Configure SAML organization slug to automatically redirect all users of your Infisical instance to the identity provider. diff --git a/frontend/src/hooks/api/admin/types.ts b/frontend/src/hooks/api/admin/types.ts index c7022a1d7..f5aaabc83 100644 --- a/frontend/src/hooks/api/admin/types.ts +++ b/frontend/src/hooks/api/admin/types.ts @@ -3,6 +3,8 @@ export type TServerConfig = { allowSignUp: boolean; allowedSignUpDomain?: string | null; isMigrationModeOn?: boolean; + trustSamlEmails: boolean; + trustLdapEmails: boolean; }; export type TCreateAdminUserDTO = { diff --git a/frontend/src/views/admin/DashboardPage/DashboardPage.tsx b/frontend/src/views/admin/DashboardPage/DashboardPage.tsx index d5bc4097e..ce1b117f3 100644 --- a/frontend/src/views/admin/DashboardPage/DashboardPage.tsx +++ b/frontend/src/views/admin/DashboardPage/DashboardPage.tsx @@ -14,11 +14,11 @@ import { Input, Select, SelectItem, + Switch, Tab, TabList, TabPanel, - Tabs -} from "@app/components/v2"; + Tabs} from "@app/components/v2"; import { useOrganization, useServerConfig, useUser } from "@app/context"; import { useUpdateServerConfig } from "@app/hooks/api"; @@ -33,7 +33,9 @@ enum SignUpModes { const formSchema = z.object({ signUpMode: z.nativeEnum(SignUpModes), - allowedSignUpDomain: z.string().optional().nullable() + allowedSignUpDomain: z.string().optional().nullable(), + trustSamlEmails: z.boolean(), + trustLdapEmails: z.boolean() }); type TDashboardForm = z.infer; @@ -52,7 +54,9 @@ export const AdminDashboardPage = () => { values: { // eslint-disable-next-line signUpMode: config.allowSignUp ? SignUpModes.Anyone : SignUpModes.Disabled, - allowedSignUpDomain: config.allowedSignUpDomain + allowedSignUpDomain: config.allowedSignUpDomain, + trustSamlEmails: config.trustSamlEmails, + trustLdapEmails: config.trustLdapEmails } }); @@ -62,8 +66,6 @@ export const AdminDashboardPage = () => { const { orgs } = useOrganization(); const { mutateAsync: updateServerConfig } = useUpdateServerConfig(); - - const isNotAllowed = !user?.superAdmin; // TODO(akhilmhdh): on nextjs 14 roadmap this will be properly addressed with context split @@ -78,10 +80,13 @@ export const AdminDashboardPage = () => { const onFormSubmit = async (formData: TDashboardForm) => { try { - const { signUpMode, allowedSignUpDomain } = formData; + const { signUpMode, allowedSignUpDomain, trustSamlEmails, trustLdapEmails } = formData; + await updateServerConfig({ allowSignUp: signUpMode !== SignUpModes.Disabled, - allowedSignUpDomain: signUpMode === SignUpModes.Anyone ? allowedSignUpDomain : null + allowedSignUpDomain: signUpMode === SignUpModes.Anyone ? allowedSignUpDomain : null, + trustSamlEmails, + trustLdapEmails }); createNotification({ text: "Successfully changed sign up setting.", @@ -123,8 +128,9 @@ export const AdminDashboardPage = () => {
Allow user signups
-
- Select if you want users to be able to signup freely into your Infisical instance. +
+ Select if you want users to be able to signup freely into your Infisical + instance.
{ />
)} +
+
Trust emails
+
+ Select if you want Infisical to trust external emails from SAML/LDAP identity + providers. If set to false, then Infisical will prompt SAML/LDAP provisioned + users to verify their email upon their first login. +
+ { + return ( + + field.onChange(value)} + isChecked={field.value} + > +

Trust SAML emails

+
+
+ ); + }} + /> + { + return ( + + field.onChange(value)} + isChecked={field.value} + > +

Trust LDAP emails

+
+
+ ); + }} + /> +