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

View File

@@ -3,9 +3,32 @@ import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
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<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");
});
}
}

View File

@@ -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<typeof SuperAdminSchema>;

View File

@@ -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: [],

View File

@@ -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: [],

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 { 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<typeof scimServiceFactory>;
// 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: [],

View File

@@ -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()),

View File

@@ -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({

View File

@@ -369,16 +369,6 @@ To login into Infisical with OAuth providers such as Google, configure the assoc
information.
</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">
Configure SAML organization slug to automatically redirect all users of your
Infisical instance to the identity provider.

View File

@@ -3,6 +3,8 @@ export type TServerConfig = {
allowSignUp: boolean;
allowedSignUpDomain?: string | null;
isMigrationModeOn?: boolean;
trustSamlEmails: boolean;
trustLdapEmails: boolean;
};
export type TCreateAdminUserDTO = {

View File

@@ -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<typeof formSchema>;
@@ -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 = () => {
<div className="mb-2 text-xl font-semibold text-mineshaft-100">
Allow user signups
</div>
<div className="mb-4 text-sm max-w-sm text-mineshaft-400">
Select if you want users to be able to signup freely into your Infisical instance.
<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.
</div>
<Controller
control={control}
@@ -176,6 +182,48 @@ export const AdminDashboardPage = () => {
/>
</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
type="submit"
isLoading={isSubmitting}