feat: allow the ability to disable relations from one side of the relation graph

Signed-off-by: benjdlambert <ben@blam.sh>
This commit is contained in:
benjdlambert
2025-05-01 14:40:05 +02:00
committed by Fredrik Adelöw
parent 2071dd40cf
commit f77c481d4a
4 changed files with 232 additions and 7 deletions
+111 -1
View File
@@ -93,6 +93,17 @@ export interface Config {
pagePause?: boolean;
};
};
/**
* Additional parsing config
*/
parsing?: {
/**
* Whether to skip the memberOf attribute on the users to power the relations of users and groups
*
* @default false
*/
skipMemberOf?: boolean;
};
/**
* JSON paths (on a.b.c form) and hard coded values to set on those
* paths.
@@ -173,6 +184,17 @@ export interface Config {
pagePause?: boolean;
};
};
/**
* Additional parsing config
*/
parsing?: {
/**
* Whether to skip the memberOf attribute on the users to power the relations of users and groups
*
* @default false
*/
skipMemberOf?: boolean;
};
/**
* JSON paths (on a.b.c form) and hard coded values to set on those
* paths.
@@ -258,6 +280,17 @@ export interface Config {
pagePause?: boolean;
};
};
/**
* Additional parsing config
*/
parsing?: {
/**
* Whether to skip the member attributes on the groups to power the relations of users and groups
*
* @default false
*/
skipMembers?: boolean;
};
/**
* JSON paths (on a.b.c form) and hard coded values to set on those
* paths.
@@ -348,6 +381,17 @@ export interface Config {
pagePause?: boolean;
};
};
/**
* Additional parsing config
*/
parsing?: {
/**
* Whether to skip the member attributes on the groups to power the relations of users and groups
*
* @default false
*/
skipMembers?: boolean;
};
/**
* JSON paths (on a.b.c form) and hard coded values to set on those
* paths.
@@ -508,6 +552,17 @@ export interface Config {
pagePause?: boolean;
};
};
/**
* Additional parsing config
*/
parsing?: {
/**
* Whether to skip the memberOf attribute on the users to power the relations of users and groups
*
* @default false
*/
skipMemberOf?: boolean;
};
/**
* JSON paths (on a.b.c form) and hard coded values to set on those
* paths.
@@ -588,6 +643,17 @@ export interface Config {
pagePause?: boolean;
};
};
/**
* Additional parsing config
*/
parsing?: {
/**
* Whether to skip the memberOf attribute on the users to power the relations of users and groups
*
* @default false
*/
skipMemberOf?: boolean;
};
/**
* JSON paths (on a.b.c form) and hard coded values to set on those
* paths.
@@ -673,6 +739,17 @@ export interface Config {
pagePause?: boolean;
};
};
/**
* Additional parsing config
*/
parsing?: {
/**
* Whether to skip the member attributes on the groups to power the relations of users and groups
*
* @default false
*/
skipMembers?: boolean;
};
/**
* JSON paths (on a.b.c form) and hard coded values to set on those
* paths.
@@ -763,6 +840,17 @@ export interface Config {
pagePause?: boolean;
};
};
/**
* Additional parsing config
*/
parsing?: {
/**
* Whether to skip the member attributes on the groups to power the relations of users and groups
*
* @default false
*/
skipMembers?: boolean;
};
/**
* JSON paths (on a.b.c form) and hard coded values to set on those
* paths.
@@ -921,12 +1009,22 @@ export interface Config {
pagePause?: boolean;
};
};
/**
* Additional parsing config
*/
parsing?: {
/**
* Whether to skip the memberOf attribute on the users to power the relations of users and groups
*
* @default false
*/
skipMemberOf?: boolean;
};
/**
* JSON paths (on a.b.c form) and hard coded values to set on those
* paths.
*
* This can be useful for example if you want to hard code a
* namespace or similar on the generated entities.
*/
set?: { [key: string]: JsonValue };
/**
@@ -1006,6 +1104,18 @@ export interface Config {
};
};
/**
* Additional parsing config
*/
parsing?: {
/**
* Whether to skip the member attributes on the groups to power the relations of users and groups
*
* @default false
*/
skipMembers?: boolean;
};
/**
* @default false
* JSON paths (on a.b.c form) and hard coded values to set on those
* paths.
*
@@ -90,6 +90,12 @@ export type UserConfig = {
// Only the scope, filter, attributes, and paged fields are supported. The
// default is scope "one" and attributes "*" and "+".
options: SearchOptions;
// Additional parsing config
parsing?: {
// Whether to skip the memberOf attribute on the users to power the relations of users and groups
skipMemberOf?: boolean;
};
// JSON paths (on a.b.c form) and hard coded values to set on those paths
set?: { [path: string]: JsonValue };
// Mappings from well known entity fields, to LDAP attribute names
@@ -129,6 +135,12 @@ export type GroupConfig = {
// The search options to use.
// Only the scope, filter, attributes, and paged fields are supported.
options: SearchOptions;
// Additional parsing config
parsing?: {
// Whether to skip the members attribute on the groups to power the relations of users and groups
skipMembers?: boolean;
};
// JSON paths (on a.b.c form) and hard coded values to set on those paths
set?: { [path: string]: JsonValue };
// Mappings from well known entity fields, to LDAP attribute names
@@ -210,6 +210,55 @@ describe('readLdapUsers', () => {
);
});
it('should allow skipping memberOf', async () => {
client.getVendor.mockResolvedValue(DefaultLdapVendor);
client.searchStreaming.mockImplementation(async (_dn, _opts, fn) => {
await fn(searchEntry({ memberOf: ['x', 'y', 'z'] }));
});
client.getVendor.mockResolvedValue(DefaultLdapVendor);
client.searchStreaming.mockImplementation(async (_dn, _opts, fn) => {
await fn(
searchEntry({
uid: ['uid-value'],
description: ['description-value'],
cn: ['cn-value'],
mail: ['mail-value'],
avatarUrl: ['avatarUrl-value'],
memberOf: ['x', 'y', 'z'],
customDN: ['dn-value'],
customUUID: ['uuid-value'],
}),
);
});
const config: UserConfig[] = [
{
dn: 'ddd',
options: {},
parsing: {
skipMemberOf: true,
},
map: {
rdn: 'uid',
name: 'uid',
description: 'description',
displayName: 'cn',
email: 'mail',
picture: 'avatarUrl',
memberOf: 'memberOf',
},
},
];
const vendorConfig: VendorConfig = {
dnAttributeName: 'customDN',
uuidAttributeName: 'customUUID',
};
const { userMemberOf } = await readLdapUsers(client, config, vendorConfig);
expect(userMemberOf.size).toBe(0);
});
it('transfers all attributes from Microsoft Active Directory', async () => {
client.getVendor.mockResolvedValue(ActiveDirectoryVendor);
client.searchStreaming.mockImplementation(async (_dn, _opts, fn) => {
@@ -729,6 +778,54 @@ describe('readLdapGroups', () => {
);
});
it('should allow skipping members', async () => {
client.getVendor.mockResolvedValue(DefaultLdapVendor);
client.searchStreaming.mockImplementation(async (_dn, _opts, fn) => {
await fn(
searchEntry({
cn: ['cn-value'],
description: ['description-value'],
tt: ['type-value'],
mail: ['mail-value'],
avatarUrl: ['avatarUrl-value'],
memberOf: ['x', 'y', 'z'],
member: ['e', 'f', 'g'],
customDN: ['dn-value'],
customUUID: ['uuid-value'],
}),
);
});
const config: GroupConfig[] = [
{
dn: 'ddd',
options: {},
parsing: {
skipMembers: true,
},
map: {
rdn: 'cn',
name: 'cn',
description: 'description',
displayName: 'cn',
email: 'mail',
picture: 'avatarUrl',
type: 'tt',
memberOf: 'memberOf',
members: 'member',
},
},
];
const vendorConfig: VendorConfig = {
dnAttributeName: 'customDN',
uuidAttributeName: 'customUUID',
};
const { groupMember } = await readLdapGroups(client, config, vendorConfig);
expect(groupMember.size).toBe(0);
});
it('can process a list of GroupConfigs', async () => {
client.getVendor.mockResolvedValue(DefaultLdapVendor);
client.searchStreaming.mockImplementation(async (_dn, _opts, fn) => {
@@ -143,9 +143,12 @@ export async function readLdapUsers(
return;
}
mapReferencesAttr(user, vendor, map.memberOf, (myDn, vs) => {
ensureItems(userMemberOf, myDn, vs);
});
if (!cfg.parsing?.skipMemberOf) {
mapReferencesAttr(user, vendor, map.memberOf, (myDn, vs) => {
ensureItems(userMemberOf, myDn, vs);
});
}
entities.push(entity);
});
}
@@ -277,9 +280,12 @@ export async function readLdapGroups(
mapReferencesAttr(entry, vendor, map.memberOf, (myDn, vs) => {
ensureItems(groupMemberOf, myDn, vs);
});
mapReferencesAttr(entry, vendor, map.members, (myDn, vs) => {
ensureItems(groupMember, myDn, vs);
});
if (!cfg.parsing?.skipMembers) {
mapReferencesAttr(entry, vendor, map.members, (myDn, vs) => {
ensureItems(groupMember, myDn, vs);
});
}
groups.push(entity);
});