feat: added support for configurable ldap user identifier

This commit is contained in:
Sheen Capadngan
2024-06-26 18:36:28 +08:00
parent 2e3aedc62b
commit 2d3a4a7559
7 changed files with 66 additions and 5 deletions
@@ -0,0 +1,19 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasColumn(TableName.LdapConfig, "uniqueUserAttribute"))) {
await knex.schema.alterTable(TableName.LdapConfig, (tb) => {
tb.string("uniqueUserAttribute").notNullable().defaultTo("");
});
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasColumn(TableName.LdapConfig, "uniqueUserAttribute")) {
await knex.schema.alterTable(TableName.LdapConfig, (t) => {
t.dropColumn("uniqueUserAttribute");
});
}
}
+2 -1
View File
@@ -26,7 +26,8 @@ export const LdapConfigsSchema = z.object({
updatedAt: z.date(), updatedAt: z.date(),
groupSearchBase: z.string().default(""), groupSearchBase: z.string().default(""),
groupSearchFilter: z.string().default(""), groupSearchFilter: z.string().default(""),
searchFilter: z.string().default("") searchFilter: z.string().default(""),
uniqueUserAttribute: z.string().default("")
}); });
export type TLdapConfigs = z.infer<typeof LdapConfigsSchema>; export type TLdapConfigs = z.infer<typeof LdapConfigsSchema>;
+8 -2
View File
@@ -70,10 +70,13 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
groups = await searchGroups(ldapConfig, groupSearchFilter, ldapConfig.groupSearchBase); groups = await searchGroups(ldapConfig, groupSearchFilter, ldapConfig.groupSearchBase);
} }
const externalId = ldapConfig.uniqueUserAttribute ? user[ldapConfig.uniqueUserAttribute] : user.uidNumber;
const username = ldapConfig.uniqueUserAttribute ? externalId : user.uid;
const { isUserCompleted, providerAuthToken } = await server.services.ldap.ldapLogin({ const { isUserCompleted, providerAuthToken } = await server.services.ldap.ldapLogin({
externalId,
username,
ldapConfigId: ldapConfig.id, ldapConfigId: ldapConfig.id,
externalId: user.uidNumber,
username: user.uid,
firstName: user.givenName ?? user.cn ?? "", firstName: user.givenName ?? user.cn ?? "",
lastName: user.sn ?? "", lastName: user.sn ?? "",
email: user.mail, email: user.mail,
@@ -138,6 +141,7 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
url: z.string(), url: z.string(),
bindDN: z.string(), bindDN: z.string(),
bindPass: z.string(), bindPass: z.string(),
uniqueUserAttribute: z.string(),
searchBase: z.string(), searchBase: z.string(),
searchFilter: z.string(), searchFilter: z.string(),
groupSearchBase: z.string(), groupSearchBase: z.string(),
@@ -172,6 +176,7 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
url: z.string().trim(), url: z.string().trim(),
bindDN: z.string().trim(), bindDN: z.string().trim(),
bindPass: z.string().trim(), bindPass: z.string().trim(),
uniqueUserAttribute: z.string().trim().default("uidNumber"),
searchBase: z.string().trim(), searchBase: z.string().trim(),
searchFilter: z.string().trim().default("(uid={{username}})"), searchFilter: z.string().trim().default("(uid={{username}})"),
groupSearchBase: z.string().trim(), groupSearchBase: z.string().trim(),
@@ -213,6 +218,7 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
url: z.string().trim(), url: z.string().trim(),
bindDN: z.string().trim(), bindDN: z.string().trim(),
bindPass: z.string().trim(), bindPass: z.string().trim(),
uniqueUserAttribute: z.string().trim(),
searchBase: z.string().trim(), searchBase: z.string().trim(),
searchFilter: z.string().trim(), searchFilter: z.string().trim(),
groupSearchBase: z.string().trim(), groupSearchBase: z.string().trim(),
@@ -122,6 +122,7 @@ export const ldapConfigServiceFactory = ({
url, url,
bindDN, bindDN,
bindPass, bindPass,
uniqueUserAttribute,
searchBase, searchBase,
searchFilter, searchFilter,
groupSearchBase, groupSearchBase,
@@ -200,6 +201,7 @@ export const ldapConfigServiceFactory = ({
encryptedBindPass, encryptedBindPass,
bindPassIV, bindPassIV,
bindPassTag, bindPassTag,
uniqueUserAttribute,
searchBase, searchBase,
searchFilter, searchFilter,
groupSearchBase, groupSearchBase,
@@ -222,6 +224,7 @@ export const ldapConfigServiceFactory = ({
url, url,
bindDN, bindDN,
bindPass, bindPass,
uniqueUserAttribute,
searchBase, searchBase,
searchFilter, searchFilter,
groupSearchBase, groupSearchBase,
@@ -244,7 +247,8 @@ export const ldapConfigServiceFactory = ({
searchBase, searchBase,
searchFilter, searchFilter,
groupSearchBase, groupSearchBase,
groupSearchFilter groupSearchFilter,
uniqueUserAttribute
}; };
const orgBot = await orgBotDAL.findOne({ orgId }); const orgBot = await orgBotDAL.findOne({ orgId });
@@ -345,6 +349,7 @@ export const ldapConfigServiceFactory = ({
url: ldapConfig.url, url: ldapConfig.url,
bindDN, bindDN,
bindPass, bindPass,
uniqueUserAttribute: ldapConfig.uniqueUserAttribute,
searchBase: ldapConfig.searchBase, searchBase: ldapConfig.searchBase,
searchFilter: ldapConfig.searchFilter, searchFilter: ldapConfig.searchFilter,
groupSearchBase: ldapConfig.groupSearchBase, groupSearchBase: ldapConfig.groupSearchBase,
@@ -381,6 +386,7 @@ export const ldapConfigServiceFactory = ({
url: ldapConfig.url, url: ldapConfig.url,
bindDN: ldapConfig.bindDN, bindDN: ldapConfig.bindDN,
bindCredentials: ldapConfig.bindPass, bindCredentials: ldapConfig.bindPass,
uniqueUserAttribute: ldapConfig.uniqueUserAttribute,
searchBase: ldapConfig.searchBase, searchBase: ldapConfig.searchBase,
searchFilter: ldapConfig.searchFilter || "(uid={{username}})", searchFilter: ldapConfig.searchFilter || "(uid={{username}})",
// searchAttributes: ["uid", "uidNumber", "givenName", "sn", "mail"], // searchAttributes: ["uid", "uidNumber", "givenName", "sn", "mail"],
@@ -7,6 +7,7 @@ export type TLDAPConfig = {
url: string; url: string;
bindDN: string; bindDN: string;
bindPass: string; bindPass: string;
uniqueUserAttribute: string;
searchBase: string; searchBase: string;
groupSearchBase: string; groupSearchBase: string;
groupSearchFilter: string; groupSearchFilter: string;
@@ -19,6 +20,7 @@ export type TCreateLdapCfgDTO = {
url: string; url: string;
bindDN: string; bindDN: string;
bindPass: string; bindPass: string;
uniqueUserAttribute: string;
searchBase: string; searchBase: string;
searchFilter: string; searchFilter: string;
groupSearchBase: string; groupSearchBase: string;
@@ -33,6 +35,7 @@ export type TUpdateLdapCfgDTO = {
url: string; url: string;
bindDN: string; bindDN: string;
bindPass: string; bindPass: string;
uniqueUserAttribute: string;
searchBase: string; searchBase: string;
searchFilter: string; searchFilter: string;
groupSearchBase: string; groupSearchBase: string;
@@ -13,6 +13,7 @@ export const useCreateLDAPConfig = () => {
url, url,
bindDN, bindDN,
bindPass, bindPass,
uniqueUserAttribute,
searchBase, searchBase,
searchFilter, searchFilter,
groupSearchBase, groupSearchBase,
@@ -24,6 +25,7 @@ export const useCreateLDAPConfig = () => {
url: string; url: string;
bindDN: string; bindDN: string;
bindPass: string; bindPass: string;
uniqueUserAttribute: string;
searchBase: string; searchBase: string;
searchFilter: string; searchFilter: string;
groupSearchBase: string; groupSearchBase: string;
@@ -36,6 +38,7 @@ export const useCreateLDAPConfig = () => {
url, url,
bindDN, bindDN,
bindPass, bindPass,
uniqueUserAttribute,
searchBase, searchBase,
searchFilter, searchFilter,
groupSearchBase, groupSearchBase,
@@ -60,6 +63,7 @@ export const useUpdateLDAPConfig = () => {
url, url,
bindDN, bindDN,
bindPass, bindPass,
uniqueUserAttribute,
searchBase, searchBase,
searchFilter, searchFilter,
groupSearchBase, groupSearchBase,
@@ -71,6 +75,7 @@ export const useUpdateLDAPConfig = () => {
url?: string; url?: string;
bindDN?: string; bindDN?: string;
bindPass?: string; bindPass?: string;
uniqueUserAttribute: string;
searchBase?: string; searchBase?: string;
searchFilter?: string; searchFilter?: string;
groupSearchBase?: string; groupSearchBase?: string;
@@ -83,6 +88,7 @@ export const useUpdateLDAPConfig = () => {
url, url,
bindDN, bindDN,
bindPass, bindPass,
uniqueUserAttribute,
searchBase, searchBase,
searchFilter, searchFilter,
groupSearchBase, groupSearchBase,
@@ -20,6 +20,7 @@ const LDAPFormSchema = z.object({
bindPass: z.string().default(""), bindPass: z.string().default(""),
searchBase: z.string().default(""), searchBase: z.string().default(""),
searchFilter: z.string().default(""), searchFilter: z.string().default(""),
uniqueUserAttribute: z.string().default(""),
groupSearchBase: z.string().default(""), groupSearchBase: z.string().default(""),
groupSearchFilter: z.string().default(""), groupSearchFilter: z.string().default(""),
caCert: z.string().optional() caCert: z.string().optional()
@@ -53,6 +54,7 @@ export const LDAPModal = ({ popUp, handlePopUpClose, handlePopUpToggle }: Props)
const watchGroupSearchBase = watch("groupSearchBase"); const watchGroupSearchBase = watch("groupSearchBase");
const watchGroupSearchFilter = watch("groupSearchFilter"); const watchGroupSearchFilter = watch("groupSearchFilter");
const watchCaCert = watch("caCert"); const watchCaCert = watch("caCert");
const watchUniqueUserAttribute = watch("uniqueUserAttribute");
useEffect(() => { useEffect(() => {
if (data) { if (data) {
@@ -64,7 +66,8 @@ export const LDAPModal = ({ popUp, handlePopUpClose, handlePopUpToggle }: Props)
searchFilter: data?.searchFilter ?? "", searchFilter: data?.searchFilter ?? "",
groupSearchBase: data?.groupSearchBase ?? "", groupSearchBase: data?.groupSearchBase ?? "",
groupSearchFilter: data?.groupSearchFilter ?? "", groupSearchFilter: data?.groupSearchFilter ?? "",
caCert: data?.caCert ?? "" caCert: data?.caCert ?? "",
uniqueUserAttribute: data?.uniqueUserAttribute ?? ""
}); });
} }
}, [data]); }, [data]);
@@ -73,6 +76,7 @@ export const LDAPModal = ({ popUp, handlePopUpClose, handlePopUpToggle }: Props)
url, url,
bindDN, bindDN,
bindPass, bindPass,
uniqueUserAttribute,
searchBase, searchBase,
searchFilter, searchFilter,
groupSearchBase, groupSearchBase,
@@ -92,6 +96,7 @@ export const LDAPModal = ({ popUp, handlePopUpClose, handlePopUpToggle }: Props)
bindPass, bindPass,
searchBase, searchBase,
searchFilter, searchFilter,
uniqueUserAttribute,
groupSearchBase, groupSearchBase,
groupSearchFilter, groupSearchFilter,
caCert caCert
@@ -105,6 +110,7 @@ export const LDAPModal = ({ popUp, handlePopUpClose, handlePopUpToggle }: Props)
bindPass, bindPass,
searchBase, searchBase,
searchFilter, searchFilter,
uniqueUserAttribute,
groupSearchBase, groupSearchBase,
groupSearchFilter, groupSearchFilter,
caCert caCert
@@ -138,6 +144,7 @@ export const LDAPModal = ({ popUp, handlePopUpClose, handlePopUpToggle }: Props)
searchFilter: watchSearchFilter, searchFilter: watchSearchFilter,
groupSearchBase: watchGroupSearchBase, groupSearchBase: watchGroupSearchBase,
groupSearchFilter: watchGroupSearchFilter, groupSearchFilter: watchGroupSearchFilter,
uniqueUserAttribute: watchUniqueUserAttribute,
caCert: watchCaCert, caCert: watchCaCert,
shouldCloseModal: false shouldCloseModal: false
}); });
@@ -217,6 +224,19 @@ export const LDAPModal = ({ popUp, handlePopUpClose, handlePopUpToggle }: Props)
</FormControl> </FormControl>
)} )}
/> />
<Controller
control={control}
name="uniqueUserAttribute"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Unique User Attribute (Optional)"
errorText={error?.message}
isError={Boolean(error)}
>
<Input {...field} placeholder="uidNumber" />
</FormControl>
)}
/>
<Controller <Controller
control={control} control={control}
name="searchFilter" name="searchFilter"