From f77c481d4ab4a7d80bf182849b66ad21b5721bea Mon Sep 17 00:00:00 2001 From: benjdlambert Date: Thu, 1 May 2025 14:40:05 +0200 Subject: [PATCH] feat: allow the ability to disable relations from one side of the relation graph Signed-off-by: benjdlambert --- .../catalog-backend-module-ldap/config.d.ts | 112 +++++++++++++++++- .../src/ldap/config.ts | 12 ++ .../src/ldap/read.test.ts | 97 +++++++++++++++ .../src/ldap/read.ts | 18 ++- 4 files changed, 232 insertions(+), 7 deletions(-) diff --git a/plugins/catalog-backend-module-ldap/config.d.ts b/plugins/catalog-backend-module-ldap/config.d.ts index 8bd8623891..5474108700 100644 --- a/plugins/catalog-backend-module-ldap/config.d.ts +++ b/plugins/catalog-backend-module-ldap/config.d.ts @@ -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. * diff --git a/plugins/catalog-backend-module-ldap/src/ldap/config.ts b/plugins/catalog-backend-module-ldap/src/ldap/config.ts index 6fdf682b42..87951a9801 100644 --- a/plugins/catalog-backend-module-ldap/src/ldap/config.ts +++ b/plugins/catalog-backend-module-ldap/src/ldap/config.ts @@ -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 diff --git a/plugins/catalog-backend-module-ldap/src/ldap/read.test.ts b/plugins/catalog-backend-module-ldap/src/ldap/read.test.ts index 4e4f4f7cfd..f1070ad1f0 100644 --- a/plugins/catalog-backend-module-ldap/src/ldap/read.test.ts +++ b/plugins/catalog-backend-module-ldap/src/ldap/read.test.ts @@ -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) => { diff --git a/plugins/catalog-backend-module-ldap/src/ldap/read.ts b/plugins/catalog-backend-module-ldap/src/ldap/read.ts index 9b9041913d..aadaaae7cb 100644 --- a/plugins/catalog-backend-module-ldap/src/ldap/read.ts +++ b/plugins/catalog-backend-module-ldap/src/ldap/read.ts @@ -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); });