Move trust saml/ldap emails to server config

This commit is contained in:
Tuan Dang
2024-04-29 11:53:28 -07:00
parent 519403023a
commit 69c50af14e
10 changed files with 102 additions and 34 deletions
@@ -3,9 +3,32 @@ import { Knex } from "knex";
import { TableName } from "../schemas"; import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> { export async function up(knex: Knex): Promise<void> {
await knex.schema.alterTable(TableName.UserAliases, (t) => { const isUserAliasTablePresent = await knex.schema.hasTable(TableName.SuperAdmin);
t.string("username").nullable().alter(); 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<void> {} export async function down(knex: Knex): Promise<void> {
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");
});
}
}
+3 -1
View File
@@ -14,7 +14,9 @@ export const SuperAdminSchema = z.object({
createdAt: z.date(), createdAt: z.date(),
updatedAt: z.date(), updatedAt: z.date(),
allowedSignUpDomain: z.string().nullable().optional(), 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<typeof SuperAdminSchema>; export type TSuperAdmin = z.infer<typeof SuperAdminSchema>;
@@ -28,6 +28,7 @@ import { TOrgDALFactory } from "@app/services/org/org-dal";
import { TProjectDALFactory } from "@app/services/project/project-dal"; import { TProjectDALFactory } from "@app/services/project/project-dal";
import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal"; import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal";
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-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 { TUserDALFactory } from "@app/services/user/user-dal";
import { normalizeUsername } from "@app/services/user/user-fns"; import { normalizeUsername } from "@app/services/user/user-fns";
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal"; import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
@@ -392,6 +393,7 @@ export const ldapConfigServiceFactory = ({
relayState relayState
}: TLdapLoginDTO) => { }: TLdapLoginDTO) => {
const appCfg = getConfig(); const appCfg = getConfig();
const serverCfg = await getServerCfg();
let userAlias = await userAliasDAL.findOne({ let userAlias = await userAliasDAL.findOne({
externalId, externalId,
orgId, orgId,
@@ -437,7 +439,7 @@ export const ldapConfigServiceFactory = ({
{ {
username: uniqueUsername, username: uniqueUsername,
email: emails[0], email: emails[0],
isEmailVerified: appCfg.TRUST_LDAP_EMAILS, isEmailVerified: serverCfg.trustLdapEmails,
firstName, firstName,
lastName, lastName,
authMethods: [], authMethods: [],
@@ -24,6 +24,7 @@ import { AuthTokenType } from "@app/services/auth/auth-type";
import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal"; import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal";
import { TOrgDALFactory } from "@app/services/org/org-dal"; import { TOrgDALFactory } from "@app/services/org/org-dal";
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-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 { TUserDALFactory } from "@app/services/user/user-dal";
import { normalizeUsername } from "@app/services/user/user-fns"; import { normalizeUsername } from "@app/services/user/user-fns";
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal"; import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
@@ -323,6 +324,7 @@ export const samlConfigServiceFactory = ({
relayState relayState
}: TSamlLoginDTO) => { }: TSamlLoginDTO) => {
const appCfg = getConfig(); const appCfg = getConfig();
const serverCfg = await getServerCfg();
const userAlias = await userAliasDAL.findOne({ const userAlias = await userAliasDAL.findOne({
externalId, externalId,
orgId, orgId,
@@ -374,7 +376,7 @@ export const samlConfigServiceFactory = ({
{ {
username: uniqueUsername, username: uniqueUsername,
email, email,
isEmailVerified: appCfg.TRUST_SAML_EMAILS, isEmailVerified: serverCfg.trustSamlEmails,
firstName, firstName,
lastName, lastName,
authMethods: [], authMethods: [],
+3 -3
View File
@@ -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 { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal"; import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service"; 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 { TUserDALFactory } from "@app/services/user/user-dal";
import { normalizeUsername } from "@app/services/user/user-fns"; import { normalizeUsername } from "@app/services/user/user-fns";
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal"; import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
@@ -81,8 +82,6 @@ type TScimServiceFactoryDep = {
export type TScimServiceFactory = ReturnType<typeof scimServiceFactory>; export type TScimServiceFactory = ReturnType<typeof scimServiceFactory>;
// TODO: finish updating all userId refs to orgMembershipId
export const scimServiceFactory = ({ export const scimServiceFactory = ({
licenseService, licenseService,
scimDAL, scimDAL,
@@ -279,6 +278,7 @@ export const scimServiceFactory = ({
}); });
const appCfg = getConfig(); const appCfg = getConfig();
const serverCfg = await getServerCfg();
const userAlias = await userAliasDAL.findOne({ const userAlias = await userAliasDAL.findOne({
externalId: username, externalId: username,
@@ -325,7 +325,7 @@ export const scimServiceFactory = ({
{ {
username: uniqueUsername, username: uniqueUsername,
email, email,
isEmailVerified: appCfg.TRUST_SAML_EMAILS, isEmailVerified: serverCfg.trustSamlEmails,
firstName, firstName,
lastName, lastName,
authMethods: [], authMethods: [],
-3
View File
@@ -98,9 +98,6 @@ const envSchema = z
CLIENT_ID_GITLAB: zpStr(z.string().optional()), CLIENT_ID_GITLAB: zpStr(z.string().optional()),
CLIENT_SECRET_GITLAB: zpStr(z.string().optional()), CLIENT_SECRET_GITLAB: zpStr(z.string().optional()),
URL_GITLAB_URL: zpStr(z.string().optional().default(GITLAB_URL)), 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
SECRET_SCANNING_WEBHOOK_PROXY: zpStr(z.string().optional()), SECRET_SCANNING_WEBHOOK_PROXY: zpStr(z.string().optional()),
SECRET_SCANNING_WEBHOOK_SECRET: zpStr(z.string().optional()), SECRET_SCANNING_WEBHOOK_SECRET: zpStr(z.string().optional()),
+3 -1
View File
@@ -42,7 +42,9 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
schema: { schema: {
body: z.object({ body: z.object({
allowSignUp: z.boolean().optional(), allowSignUp: z.boolean().optional(),
allowedSignUpDomain: z.string().optional().nullable() allowedSignUpDomain: z.string().optional().nullable(),
trustSamlEmails: z.boolean().optional(),
trustLdapEmails: z.boolean().optional()
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -369,16 +369,6 @@ To login into Infisical with OAuth providers such as Google, configure the assoc
information. information.
</Accordion> </Accordion>
<ParamField query="TRUST_SAML_EMAILS" type="boolean" default="false" optional>
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.
</ParamField>
<ParamField query="TRUST_LDAP_EMAILS" type="string" default="false" optional>
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.
</ParamField>
<ParamField query="NEXT_PUBLIC_SAML_ORG_SLUG" type="string"> <ParamField query="NEXT_PUBLIC_SAML_ORG_SLUG" type="string">
Configure SAML organization slug to automatically redirect all users of your Configure SAML organization slug to automatically redirect all users of your
Infisical instance to the identity provider. Infisical instance to the identity provider.
+2
View File
@@ -3,6 +3,8 @@ export type TServerConfig = {
allowSignUp: boolean; allowSignUp: boolean;
allowedSignUpDomain?: string | null; allowedSignUpDomain?: string | null;
isMigrationModeOn?: boolean; isMigrationModeOn?: boolean;
trustSamlEmails: boolean;
trustLdapEmails: boolean;
}; };
export type TCreateAdminUserDTO = { export type TCreateAdminUserDTO = {
@@ -14,11 +14,11 @@ import {
Input, Input,
Select, Select,
SelectItem, SelectItem,
Switch,
Tab, Tab,
TabList, TabList,
TabPanel, TabPanel,
Tabs Tabs} from "@app/components/v2";
} from "@app/components/v2";
import { useOrganization, useServerConfig, useUser } from "@app/context"; import { useOrganization, useServerConfig, useUser } from "@app/context";
import { useUpdateServerConfig } from "@app/hooks/api"; import { useUpdateServerConfig } from "@app/hooks/api";
@@ -33,7 +33,9 @@ enum SignUpModes {
const formSchema = z.object({ const formSchema = z.object({
signUpMode: z.nativeEnum(SignUpModes), signUpMode: z.nativeEnum(SignUpModes),
allowedSignUpDomain: z.string().optional().nullable() allowedSignUpDomain: z.string().optional().nullable(),
trustSamlEmails: z.boolean(),
trustLdapEmails: z.boolean()
}); });
type TDashboardForm = z.infer<typeof formSchema>; type TDashboardForm = z.infer<typeof formSchema>;
@@ -52,7 +54,9 @@ export const AdminDashboardPage = () => {
values: { values: {
// eslint-disable-next-line // eslint-disable-next-line
signUpMode: config.allowSignUp ? SignUpModes.Anyone : SignUpModes.Disabled, 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 { orgs } = useOrganization();
const { mutateAsync: updateServerConfig } = useUpdateServerConfig(); const { mutateAsync: updateServerConfig } = useUpdateServerConfig();
const isNotAllowed = !user?.superAdmin; const isNotAllowed = !user?.superAdmin;
// TODO(akhilmhdh): on nextjs 14 roadmap this will be properly addressed with context split // 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) => { const onFormSubmit = async (formData: TDashboardForm) => {
try { try {
const { signUpMode, allowedSignUpDomain } = formData; const { signUpMode, allowedSignUpDomain, trustSamlEmails, trustLdapEmails } = formData;
await updateServerConfig({ await updateServerConfig({
allowSignUp: signUpMode !== SignUpModes.Disabled, allowSignUp: signUpMode !== SignUpModes.Disabled,
allowedSignUpDomain: signUpMode === SignUpModes.Anyone ? allowedSignUpDomain : null allowedSignUpDomain: signUpMode === SignUpModes.Anyone ? allowedSignUpDomain : null,
trustSamlEmails,
trustLdapEmails
}); });
createNotification({ createNotification({
text: "Successfully changed sign up setting.", text: "Successfully changed sign up setting.",
@@ -123,8 +128,9 @@ export const AdminDashboardPage = () => {
<div className="mb-2 text-xl font-semibold text-mineshaft-100"> <div className="mb-2 text-xl font-semibold text-mineshaft-100">
Allow user signups Allow user signups
</div> </div>
<div className="mb-4 text-sm max-w-sm text-mineshaft-400"> <div className="mb-4 max-w-sm text-sm text-mineshaft-400">
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.
</div> </div>
<Controller <Controller
control={control} control={control}
@@ -176,6 +182,48 @@ export const AdminDashboardPage = () => {
/> />
</div> </div>
)} )}
<div className="mt-8 mb-8 flex flex-col justify-start">
<div className="mb-2 text-xl font-semibold text-mineshaft-100">Trust emails</div>
<div className="mb-4 max-w-sm text-sm text-mineshaft-400">
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.
</div>
<Controller
control={control}
name="trustSamlEmails"
render={({ field, fieldState: { error } }) => {
return (
<FormControl isError={Boolean(error)} errorText={error?.message}>
<Switch
id="trust-saml-emails"
onCheckedChange={(value) => field.onChange(value)}
isChecked={field.value}
>
<p className="w-full">Trust SAML emails</p>
</Switch>
</FormControl>
);
}}
/>
<Controller
control={control}
name="trustLdapEmails"
render={({ field, fieldState: { error } }) => {
return (
<FormControl isError={Boolean(error)} errorText={error?.message}>
<Switch
id="trust-ldap-emails"
onCheckedChange={(value) => field.onChange(value)}
isChecked={field.value}
>
<p className="w-full">Trust LDAP emails</p>
</Switch>
</FormControl>
);
}}
/>
</div>
<Button <Button
type="submit" type="submit"
isLoading={isSubmitting} isLoading={isSubmitting}