Update ldap group mapping schema, replace group input field with select

This commit is contained in:
Tuan Dang
2024-04-23 15:04:02 -07:00
parent 961c6391a8
commit d222bbf131
12 changed files with 123 additions and 42 deletions

View File

@@ -10,8 +10,9 @@ export async function up(knex: Knex): Promise<void> {
t.uuid("ldapConfigId").notNullable();
t.foreign("ldapConfigId").references("id").inTable(TableName.LdapConfig).onDelete("CASCADE");
t.string("ldapGroupCN").notNullable();
t.string("groupSlug").notNullable();
t.unique(["ldapGroupCN", "groupSlug", "ldapConfigId"]);
t.uuid("groupId").notNullable();
t.foreign("groupId").references("id").inTable(TableName.Groups).onDelete("CASCADE");
t.unique(["ldapGroupCN", "groupId", "ldapConfigId"]);
});
}

View File

@@ -11,7 +11,7 @@ export const LdapGroupMapsSchema = z.object({
id: z.string().uuid(),
ldapConfigId: z.string().uuid(),
ldapGroupCN: z.string(),
groupSlug: z.string()
groupId: z.string().uuid()
});
export type TLdapGroupMaps = z.infer<typeof LdapGroupMapsSchema>;

View File

@@ -71,6 +71,7 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
// If group search values are not provided, proceed directly to LDAP login
return await server.services.ldap
.ldapLogin({
ldapConfigId: ldapConfig.id,
externalId: user.uidNumber,
username: user.uid,
firstName: user.givenName,
@@ -111,6 +112,7 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
// groups here
ldapClient.unbind();
return server.services.ldap.ldapLogin({
ldapConfigId: ldapConfig.id,
externalId: user.uidNumber,
username: user.uid,
firstName: user.givenName,
@@ -292,7 +294,18 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
configId: z.string().trim()
}),
response: {
200: z.array(LdapGroupMapsSchema)
200: z.array(
z.object({
id: z.string(),
ldapConfigId: z.string(),
ldapGroupCN: z.string(),
group: z.object({
id: z.string(),
name: z.string(),
slug: z.string()
})
})
)
}
},
handler: async (req) => {

View File

@@ -37,7 +37,7 @@ import { TLdapGroupMapDALFactory } from "./ldap-group-map-dal";
type TLdapConfigServiceFactoryDep = {
ldapConfigDAL: Pick<TLdapConfigDALFactory, "create" | "update" | "findOne">;
ldapGroupMapDAL: Pick<TLdapGroupMapDALFactory, "find" | "create" | "delete">;
ldapGroupMapDAL: Pick<TLdapGroupMapDALFactory, "find" | "create" | "delete" | "findLdapGroupMapsByLdapConfigId">;
orgDAL: Pick<
TOrgDALFactory,
"createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById"
@@ -57,6 +57,7 @@ export const ldapConfigServiceFactory = ({
ldapGroupMapDAL,
orgDAL,
orgBotDAL,
groupDAL,
userDAL,
userAliasDAL,
permissionService,
@@ -343,7 +344,17 @@ export const ldapConfigServiceFactory = ({
return { opts, ldapConfig };
};
const ldapLogin = async ({ externalId, username, firstName, lastName, emails, orgId, relayState }: TLdapLoginDTO) => {
const ldapLogin = async ({
// ldapConfigId,
externalId,
username,
firstName,
lastName,
emails,
groups,
orgId,
relayState
}: TLdapLoginDTO) => {
const appCfg = getConfig();
let userAlias = await userAliasDAL.findOne({
externalId,
@@ -419,23 +430,28 @@ export const ldapConfigServiceFactory = ({
const user = await userDAL.findOne({ id: userAlias.userId });
// if (groups) { // TODO
// /**
// * TODO:
// * - Query for groups matching name
// * - Provision, de-provision user to groups accordingly
// */
// console.log("there are groups");
// const matchingGroups = await groupDAL.find({
// $in: {
// name: groups.map((group) => group.cn)
// }
// });
// console.log("found matching groups");
// }
if (groups) {
// TODO
// const m = await ldapGroupMapDAL.find({
// ldapConfigId,
// $in: {
// ldapGroupCN: groups.map((group) => group.cn)
// }
// });
/**
* TODO:
* - Find relevant group maps
* - Query for groups matching name
* - Provision, de-provision user to groups accordingly
*/
// console.log("there are groups");
// const matchingGroups = await groupDAL.find({
// $in: {
// name: groups.map((group) => group.cn)
// }
// });
// console.log("found matching groups");
}
const isUserCompleted = Boolean(user.isAccepted);
@@ -483,9 +499,7 @@ export const ldapConfigServiceFactory = ({
if (!ldapConfig) throw new BadRequestError({ message: "Failed to find organization LDAP data" });
const groupMaps = await ldapGroupMapDAL.find({
ldapConfigId
});
const groupMaps = await ldapGroupMapDAL.findLdapGroupMapsByLdapConfigId(ldapConfigId);
return groupMaps;
};
@@ -507,13 +521,15 @@ export const ldapConfigServiceFactory = ({
id: ldapConfigId,
orgId
});
if (!ldapConfig) throw new BadRequestError({ message: "Failed to find organization LDAP data" });
const group = await groupDAL.findOne({ slug: groupSlug, orgId });
if (!group) throw new BadRequestError({ message: "Failed to find group" });
const groupMap = await ldapGroupMapDAL.create({
ldapConfigId,
ldapGroupCN,
groupSlug
groupId: group.id
});
return groupMap;

View File

@@ -31,6 +31,7 @@ export type TGetLdapCfgDTO = {
} & TOrgPermission;
export type TLdapLoginDTO = {
ldapConfigId: string;
externalId: string;
username: string;
firstName: string;

View File

@@ -1,11 +1,41 @@
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { ormify } from "@app/lib/knex";
import { DatabaseError } from "@app/lib/errors";
import { ormify, selectAllTableCols } from "@app/lib/knex";
export type TLdapGroupMapDALFactory = ReturnType<typeof ldapGroupMapDALFactory>;
export const ldapGroupMapDALFactory = (db: TDbClient) => {
const ldapGroupMapOrm = ormify(db, TableName.LdapGroupMap);
return { ...ldapGroupMapOrm };
const findLdapGroupMapsByLdapConfigId = async (ldapConfigId: string) => {
try {
const docs = await db(TableName.LdapGroupMap)
.where(`${TableName.LdapGroupMap}.ldapConfigId`, ldapConfigId)
.join(TableName.Groups, `${TableName.LdapGroupMap}.groupId`, `${TableName.Groups}.id`)
.select(selectAllTableCols(TableName.LdapGroupMap))
.select(
db.ref("id").withSchema(TableName.Groups).as("groupId"),
db.ref("name").withSchema(TableName.Groups).as("groupSlug"),
db.ref("slug").withSchema(TableName.Groups).as("groupName")
);
return docs.map((doc) => {
return {
id: doc.id,
ldapConfigId: doc.ldapConfigId,
ldapGroupCN: doc.ldapGroupCN,
group: {
id: doc.groupId,
name: doc.groupName,
slug: doc.groupSlug
}
};
});
} catch (error) {
throw new DatabaseError({ error, name: "findGroupMaps" });
}
};
return { ...ldapGroupMapOrm, findLdapGroupMapsByLdapConfigId };
};

View File

@@ -20,7 +20,7 @@ export const getDefaultOnPremFeatures = () => {
samlSSO: false,
scim: false,
ldap: true,
groups: false,
groups: true,
status: null,
trial_end: null,
has_used_trial: true,

View File

@@ -27,7 +27,7 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
samlSSO: false,
scim: false,
ldap: true,
groups: false,
groups: true,
status: null,
trial_end: null,
has_used_trial: true,

View File

@@ -43,7 +43,7 @@ export type TFeatureSet = {
samlSSO: false;
scim: false;
ldap: true;
groups: false;
groups: true;
status: null;
trial_end: null;
has_used_trial: true;

View File

@@ -2,5 +2,9 @@ export type LDAPGroupMap = {
id: string;
ldapConfigId: string;
ldapGroupCN: string;
groupSlug: string;
group: {
id: string;
name: string;
slug: string;
};
};

View File

@@ -14,6 +14,8 @@ import {
Input,
Modal,
ModalContent,
Select,
SelectItem,
Table,
TableContainer,
TableSkeleton,
@@ -27,7 +29,9 @@ import {
useCreateLDAPGroupMapping,
useDeleteLDAPGroupMapping,
useGetLDAPConfig,
useGetLDAPGroupMaps} from "@app/hooks/api";
useGetLDAPGroupMaps,
useGetOrganizationGroups
} from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp";
const schema = z.object({
@@ -56,6 +60,7 @@ export const LDAPGroupMapModal = ({ popUp, handlePopUpOpen, handlePopUpToggle }:
const { currentOrg } = useOrganization();
const { data: ldapConfig } = useGetLDAPConfig(currentOrg?.id ?? "");
const { data: groups } = useGetOrganizationGroups(currentOrg?.id ?? "");
const { data: groupMaps, isLoading } = useGetLDAPGroupMaps(ldapConfig?.id ?? "");
const { mutateAsync: createLDAPGroupMapping, isLoading: createIsLoading } =
useCreateLDAPGroupMapping();
@@ -152,15 +157,27 @@ export const LDAPGroupMapModal = ({ popUp, handlePopUpOpen, handlePopUpToggle }:
<Controller
control={control}
name="groupSlug"
render={({ field, fieldState: { error } }) => (
defaultValue=""
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl
label="Group Slug"
label="Infisical Group"
errorText={error?.message}
isError={Boolean(error)}
className="ml-4"
className="ml-4 w-full"
>
<div className="flex">
<Input {...field} placeholder="engineering" />
<Select
defaultValue={field.value}
{...field}
onValueChange={(e) => onChange(e)}
className="w-full"
>
{(groups || []).map(({ name, id, slug }) => (
<SelectItem value={slug} key={`internal-group-${id}`}>
{name}
</SelectItem>
))}
</Select>
<Button className="ml-4" size="sm" type="submit" isLoading={createIsLoading}>
Add mapping
</Button>
@@ -183,11 +200,11 @@ export const LDAPGroupMapModal = ({ popUp, handlePopUpOpen, handlePopUpToggle }:
<TBody>
{isLoading && <TableSkeleton columns={3} innerKey="ldap-group-maps" />}
{!isLoading &&
groupMaps?.map(({ id, ldapGroupCN, groupSlug }) => {
groupMaps?.map(({ id, ldapGroupCN, group: { name } }) => {
return (
<Tr className="h-10 items-center" key={`ldap-group-map-${id}`}>
<Td>{ldapGroupCN}</Td>
<Td>{groupSlug}</Td>
<Td>{name}</Td>
<Td>
<IconButton
onClick={() => {

View File

@@ -83,7 +83,6 @@ export const OrgLDAPSection = (): JSX.Element => {
};
const openLDAPGroupMapModal = () => {
console.log("openLDAPGroupMapModal sub: ", subscription);
if (!subscription?.ldap) {
handlePopUpOpen("upgradePlan");
return;