Merge pull request #25261 from Jenson3210/feature/multiple_ldap_users_groups
Added support for only users or only groups and multiple users or multiple groups bindings
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend-module-ldap': minor
|
||||
---
|
||||
|
||||
**BREAKING**: `readLdapOrg` and the `LdapProviderConfig` type now always accept arrays of user and group configs, not just single items.
|
||||
|
||||
Added support for single ldap catalog provider to provide list and undefined user and group bindings next to standard single one.
|
||||
@@ -86,6 +86,14 @@ catalog:
|
||||
These config blocks have a lot of options in them, so we will describe each
|
||||
"root" key within the block separately.
|
||||
|
||||
> NOTE:
|
||||
>
|
||||
> If you want to import users and groups from different LDAP servers, you can define multiple providers with different names.
|
||||
> If they should come from the same server, you can define multiple users and groups blocks within the same provider using an array of users / groups.
|
||||
> Entries coming from the same block will be able to detect group memberships based on the `memberOf` attribute.
|
||||
>
|
||||
> If you want only to import users or groups, you can omit the groups or users block.
|
||||
|
||||
### target
|
||||
|
||||
This is the URL of the targeted server, typically on the form
|
||||
|
||||
@@ -197,8 +197,8 @@ export type LdapProviderConfig = {
|
||||
target: string;
|
||||
tls?: TLSConfig;
|
||||
bind?: BindConfig;
|
||||
users: UserConfig;
|
||||
groups: GroupConfig;
|
||||
users: UserConfig[];
|
||||
groups: GroupConfig[];
|
||||
schedule?: SchedulerServiceTaskScheduleDefinition;
|
||||
};
|
||||
|
||||
@@ -223,8 +223,8 @@ export function readLdapLegacyConfig(config: Config): LdapProviderConfig[];
|
||||
// @public
|
||||
export function readLdapOrg(
|
||||
client: LdapClient,
|
||||
userConfig: UserConfig,
|
||||
groupConfig: GroupConfig,
|
||||
userConfig: UserConfig[],
|
||||
groupConfig: GroupConfig[],
|
||||
options: {
|
||||
groupTransformer?: GroupTransformer;
|
||||
userTransformer?: UserTransformer;
|
||||
|
||||
+342
-170
@@ -63,180 +63,352 @@ export interface Config {
|
||||
/**
|
||||
* The settings that govern the reading and interpretation of users.
|
||||
*/
|
||||
users: {
|
||||
/**
|
||||
* The DN under which users are stored.
|
||||
*
|
||||
* E.g. "ou=people,ou=example,dc=example,dc=net"
|
||||
*/
|
||||
dn: string;
|
||||
/**
|
||||
* The search options to use. The default is scope "one" and
|
||||
* attributes "*" and "+".
|
||||
*
|
||||
* It is common to want to specify a filter, to narrow down the set
|
||||
* of matching items.
|
||||
*/
|
||||
options: {
|
||||
scope?: 'base' | 'one' | 'sub';
|
||||
filter?: string;
|
||||
attributes?: string | string[];
|
||||
sizeLimit?: number;
|
||||
timeLimit?: number;
|
||||
derefAliases?: number;
|
||||
typesOnly?: boolean;
|
||||
paged?:
|
||||
| boolean
|
||||
| {
|
||||
pageSize?: number;
|
||||
pagePause?: 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 };
|
||||
/**
|
||||
* Mappings from well known entity fields, to LDAP attribute names
|
||||
*/
|
||||
map?: {
|
||||
/**
|
||||
* The name of the attribute that holds the relative
|
||||
* distinguished name of each entry. Defaults to "uid".
|
||||
*/
|
||||
rdn?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the metadata.name field of the entity. Defaults to "uid".
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the metadata.description field of the entity.
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the spec.profile.displayName field of the entity. Defaults to
|
||||
* "cn".
|
||||
*/
|
||||
displayName?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the spec.profile.email field of the entity. Defaults to
|
||||
* "mail".
|
||||
*/
|
||||
email?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the spec.profile.picture field of the entity.
|
||||
*/
|
||||
picture?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the values of
|
||||
* the spec.memberOf field of the entity. Defaults to "memberOf".
|
||||
*/
|
||||
memberOf?: string;
|
||||
};
|
||||
};
|
||||
users?:
|
||||
| {
|
||||
/**
|
||||
* The DN under which users are stored.
|
||||
*
|
||||
* E.g. "ou=people,ou=example,dc=example,dc=net"
|
||||
*/
|
||||
dn: string;
|
||||
/**
|
||||
* The search options to use. The default is scope "one" and
|
||||
* attributes "*" and "+".
|
||||
*
|
||||
* It is common to want to specify a filter, to narrow down the set
|
||||
* of matching items.
|
||||
*/
|
||||
options: {
|
||||
scope?: 'base' | 'one' | 'sub';
|
||||
filter?: string;
|
||||
attributes?: string | string[];
|
||||
sizeLimit?: number;
|
||||
timeLimit?: number;
|
||||
derefAliases?: number;
|
||||
typesOnly?: boolean;
|
||||
paged?:
|
||||
| boolean
|
||||
| {
|
||||
pageSize?: number;
|
||||
pagePause?: 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 };
|
||||
/**
|
||||
* Mappings from well known entity fields, to LDAP attribute names
|
||||
*/
|
||||
map?: {
|
||||
/**
|
||||
* The name of the attribute that holds the relative
|
||||
* distinguished name of each entry. Defaults to "uid".
|
||||
*/
|
||||
rdn?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the metadata.name field of the entity. Defaults to "uid".
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the metadata.description field of the entity.
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the spec.profile.displayName field of the entity. Defaults to
|
||||
* "cn".
|
||||
*/
|
||||
displayName?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the spec.profile.email field of the entity. Defaults to
|
||||
* "mail".
|
||||
*/
|
||||
email?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the spec.profile.picture field of the entity.
|
||||
*/
|
||||
picture?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the values of
|
||||
* the spec.memberOf field of the entity. Defaults to "memberOf".
|
||||
*/
|
||||
memberOf?: string;
|
||||
};
|
||||
}
|
||||
| Array<{
|
||||
/**
|
||||
* The DN under which users are stored.
|
||||
*
|
||||
* E.g. "ou=people,ou=example,dc=example,dc=net"
|
||||
*/
|
||||
dn: string;
|
||||
/**
|
||||
* The search options to use. The default is scope "one" and
|
||||
* attributes "*" and "+".
|
||||
*
|
||||
* It is common to want to specify a filter, to narrow down the set
|
||||
* of matching items.
|
||||
*/
|
||||
options: {
|
||||
scope?: 'base' | 'one' | 'sub';
|
||||
filter?: string;
|
||||
attributes?: string | string[];
|
||||
sizeLimit?: number;
|
||||
timeLimit?: number;
|
||||
derefAliases?: number;
|
||||
typesOnly?: boolean;
|
||||
paged?:
|
||||
| boolean
|
||||
| {
|
||||
pageSize?: number;
|
||||
pagePause?: 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 };
|
||||
/**
|
||||
* Mappings from well known entity fields, to LDAP attribute names
|
||||
*/
|
||||
map?: {
|
||||
/**
|
||||
* The name of the attribute that holds the relative
|
||||
* distinguished name of each entry. Defaults to "uid".
|
||||
*/
|
||||
rdn?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the metadata.name field of the entity. Defaults to "uid".
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the metadata.description field of the entity.
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the spec.profile.displayName field of the entity. Defaults to
|
||||
* "cn".
|
||||
*/
|
||||
displayName?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the spec.profile.email field of the entity. Defaults to
|
||||
* "mail".
|
||||
*/
|
||||
email?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the spec.profile.picture field of the entity.
|
||||
*/
|
||||
picture?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the values of
|
||||
* the spec.memberOf field of the entity. Defaults to "memberOf".
|
||||
*/
|
||||
memberOf?: string;
|
||||
};
|
||||
}>;
|
||||
|
||||
/**
|
||||
* The settings that govern the reading and interpretation of groups.
|
||||
*/
|
||||
groups: {
|
||||
/**
|
||||
* The DN under which groups are stored.
|
||||
*
|
||||
* E.g. "ou=people,ou=example,dc=example,dc=net"
|
||||
*/
|
||||
dn: string;
|
||||
/**
|
||||
* The search options to use. The default is scope "one" and
|
||||
* attributes "*" and "+".
|
||||
*
|
||||
* It is common to want to specify a filter, to narrow down the set
|
||||
* of matching items.
|
||||
*/
|
||||
options: {
|
||||
scope?: 'base' | 'one' | 'sub';
|
||||
filter?: string;
|
||||
attributes?: string | string[];
|
||||
sizeLimit?: number;
|
||||
timeLimit?: number;
|
||||
derefAliases?: number;
|
||||
typesOnly?: boolean;
|
||||
paged?:
|
||||
| boolean
|
||||
| {
|
||||
pageSize?: number;
|
||||
pagePause?: 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 };
|
||||
/**
|
||||
* Mappings from well known entity fields, to LDAP attribute names
|
||||
*/
|
||||
map?: {
|
||||
/**
|
||||
* The name of the attribute that holds the relative
|
||||
* distinguished name of each entry. Defaults to "cn".
|
||||
*/
|
||||
rdn?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the metadata.name field of the entity. Defaults to "cn".
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the metadata.description field of the entity. Defaults to
|
||||
* "description".
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the spec.type field of the entity. Defaults to "groupType".
|
||||
*/
|
||||
type?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the spec.profile.displayName field of the entity. Defaults to
|
||||
* "cn".
|
||||
*/
|
||||
displayName?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the spec.profile.email field of the entity.
|
||||
*/
|
||||
email?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the spec.profile.picture field of the entity.
|
||||
*/
|
||||
picture?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the values of
|
||||
* the spec.parent field of the entity. Defaults to "memberOf".
|
||||
*/
|
||||
memberOf?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the values of
|
||||
* the spec.children field of the entity. Defaults to "member".
|
||||
*/
|
||||
members?: string;
|
||||
};
|
||||
};
|
||||
groups?:
|
||||
| {
|
||||
/**
|
||||
* The DN under which groups are stored.
|
||||
*
|
||||
* E.g. "ou=people,ou=example,dc=example,dc=net"
|
||||
*/
|
||||
dn: string;
|
||||
/**
|
||||
* The search options to use. The default is scope "one" and
|
||||
* attributes "*" and "+".
|
||||
*
|
||||
* It is common to want to specify a filter, to narrow down the set
|
||||
* of matching items.
|
||||
*/
|
||||
options: {
|
||||
scope?: 'base' | 'one' | 'sub';
|
||||
filter?: string;
|
||||
attributes?: string | string[];
|
||||
sizeLimit?: number;
|
||||
timeLimit?: number;
|
||||
derefAliases?: number;
|
||||
typesOnly?: boolean;
|
||||
paged?:
|
||||
| boolean
|
||||
| {
|
||||
pageSize?: number;
|
||||
pagePause?: 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 };
|
||||
/**
|
||||
* Mappings from well known entity fields, to LDAP attribute names
|
||||
*/
|
||||
map?: {
|
||||
/**
|
||||
* The name of the attribute that holds the relative
|
||||
* distinguished name of each entry. Defaults to "cn".
|
||||
*/
|
||||
rdn?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the metadata.name field of the entity. Defaults to "cn".
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the metadata.description field of the entity. Defaults to
|
||||
* "description".
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the spec.type field of the entity. Defaults to "groupType".
|
||||
*/
|
||||
type?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the spec.profile.displayName field of the entity. Defaults to
|
||||
* "cn".
|
||||
*/
|
||||
displayName?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the spec.profile.email field of the entity.
|
||||
*/
|
||||
email?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the spec.profile.picture field of the entity.
|
||||
*/
|
||||
picture?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the values of
|
||||
* the spec.parent field of the entity. Defaults to "memberOf".
|
||||
*/
|
||||
memberOf?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the values of
|
||||
* the spec.children field of the entity. Defaults to "member".
|
||||
*/
|
||||
members?: string;
|
||||
};
|
||||
}
|
||||
| Array<{
|
||||
/**
|
||||
* The DN under which groups are stored.
|
||||
*
|
||||
* E.g. "ou=people,ou=example,dc=example,dc=net"
|
||||
*/
|
||||
dn: string;
|
||||
/**
|
||||
* The search options to use. The default is scope "one" and
|
||||
* attributes "*" and "+".
|
||||
*
|
||||
* It is common to want to specify a filter, to narrow down the set
|
||||
* of matching items.
|
||||
*/
|
||||
options: {
|
||||
scope?: 'base' | 'one' | 'sub';
|
||||
filter?: string;
|
||||
attributes?: string | string[];
|
||||
sizeLimit?: number;
|
||||
timeLimit?: number;
|
||||
derefAliases?: number;
|
||||
typesOnly?: boolean;
|
||||
paged?:
|
||||
| boolean
|
||||
| {
|
||||
pageSize?: number;
|
||||
pagePause?: 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 };
|
||||
/**
|
||||
* Mappings from well known entity fields, to LDAP attribute names
|
||||
*/
|
||||
map?: {
|
||||
/**
|
||||
* The name of the attribute that holds the relative
|
||||
* distinguished name of each entry. Defaults to "cn".
|
||||
*/
|
||||
rdn?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the metadata.name field of the entity. Defaults to "cn".
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the metadata.description field of the entity. Defaults to
|
||||
* "description".
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the spec.type field of the entity. Defaults to "groupType".
|
||||
*/
|
||||
type?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the spec.profile.displayName field of the entity. Defaults to
|
||||
* "cn".
|
||||
*/
|
||||
displayName?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the spec.profile.email field of the entity.
|
||||
*/
|
||||
email?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the value of
|
||||
* the spec.profile.picture field of the entity.
|
||||
*/
|
||||
picture?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the values of
|
||||
* the spec.parent field of the entity. Defaults to "memberOf".
|
||||
*/
|
||||
memberOf?: string;
|
||||
/**
|
||||
* The name of the attribute that shall be used for the values of
|
||||
* the spec.children field of the entity. Defaults to "member".
|
||||
*/
|
||||
members?: string;
|
||||
};
|
||||
}>;
|
||||
}>;
|
||||
};
|
||||
|
||||
|
||||
@@ -42,38 +42,42 @@ describe('readLdapConfig', () => {
|
||||
id: 'default',
|
||||
target: 'target',
|
||||
bind: undefined,
|
||||
users: {
|
||||
dn: 'udn',
|
||||
options: {
|
||||
scope: 'one',
|
||||
attributes: ['*', '+'],
|
||||
users: [
|
||||
{
|
||||
dn: 'udn',
|
||||
options: {
|
||||
scope: 'one',
|
||||
attributes: ['*', '+'],
|
||||
},
|
||||
set: undefined,
|
||||
map: {
|
||||
rdn: 'uid',
|
||||
name: 'uid',
|
||||
displayName: 'cn',
|
||||
email: 'mail',
|
||||
memberOf: 'memberOf',
|
||||
},
|
||||
},
|
||||
set: undefined,
|
||||
map: {
|
||||
rdn: 'uid',
|
||||
name: 'uid',
|
||||
displayName: 'cn',
|
||||
email: 'mail',
|
||||
memberOf: 'memberOf',
|
||||
],
|
||||
groups: [
|
||||
{
|
||||
dn: 'gdn',
|
||||
options: {
|
||||
scope: 'one',
|
||||
attributes: ['*', '+'],
|
||||
},
|
||||
set: undefined,
|
||||
map: {
|
||||
rdn: 'cn',
|
||||
name: 'cn',
|
||||
description: 'description',
|
||||
type: 'groupType',
|
||||
displayName: 'cn',
|
||||
memberOf: 'memberOf',
|
||||
members: 'member',
|
||||
},
|
||||
},
|
||||
},
|
||||
groups: {
|
||||
dn: 'gdn',
|
||||
options: {
|
||||
scope: 'one',
|
||||
attributes: ['*', '+'],
|
||||
},
|
||||
set: undefined,
|
||||
map: {
|
||||
rdn: 'cn',
|
||||
name: 'cn',
|
||||
description: 'description',
|
||||
type: 'groupType',
|
||||
displayName: 'cn',
|
||||
memberOf: 'memberOf',
|
||||
members: 'member',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
expect(actual).toEqual(expected);
|
||||
@@ -111,38 +115,42 @@ describe('readLdapConfig', () => {
|
||||
frequency: { minutes: 3 },
|
||||
timeout: { minutes: 1 },
|
||||
},
|
||||
users: {
|
||||
dn: 'udn',
|
||||
options: {
|
||||
scope: 'one',
|
||||
attributes: ['*', '+'],
|
||||
users: [
|
||||
{
|
||||
dn: 'udn',
|
||||
options: {
|
||||
scope: 'one',
|
||||
attributes: ['*', '+'],
|
||||
},
|
||||
set: undefined,
|
||||
map: {
|
||||
rdn: 'uid',
|
||||
name: 'uid',
|
||||
displayName: 'cn',
|
||||
email: 'mail',
|
||||
memberOf: 'memberOf',
|
||||
},
|
||||
},
|
||||
set: undefined,
|
||||
map: {
|
||||
rdn: 'uid',
|
||||
name: 'uid',
|
||||
displayName: 'cn',
|
||||
email: 'mail',
|
||||
memberOf: 'memberOf',
|
||||
],
|
||||
groups: [
|
||||
{
|
||||
dn: 'gdn',
|
||||
options: {
|
||||
scope: 'one',
|
||||
attributes: ['*', '+'],
|
||||
},
|
||||
set: undefined,
|
||||
map: {
|
||||
rdn: 'cn',
|
||||
name: 'cn',
|
||||
description: 'description',
|
||||
type: 'groupType',
|
||||
displayName: 'cn',
|
||||
memberOf: 'memberOf',
|
||||
members: 'member',
|
||||
},
|
||||
},
|
||||
},
|
||||
groups: {
|
||||
dn: 'gdn',
|
||||
options: {
|
||||
scope: 'one',
|
||||
attributes: ['*', '+'],
|
||||
},
|
||||
set: undefined,
|
||||
map: {
|
||||
rdn: 'cn',
|
||||
name: 'cn',
|
||||
description: 'description',
|
||||
type: 'groupType',
|
||||
displayName: 'cn',
|
||||
memberOf: 'memberOf',
|
||||
members: 'member',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
expect(actual).toEqual(expected);
|
||||
@@ -228,57 +236,61 @@ describe('readLdapConfig', () => {
|
||||
keys: '/tmp/keys.pem',
|
||||
certs: '/tmp/certs.pem',
|
||||
},
|
||||
users: {
|
||||
dn: 'udn',
|
||||
options: {
|
||||
scope: 'base',
|
||||
attributes: ['*'],
|
||||
filter: 'f',
|
||||
paged: true,
|
||||
timeLimit: 42,
|
||||
sizeLimit: 100,
|
||||
derefAliases: 0,
|
||||
typesOnly: false,
|
||||
},
|
||||
set: { p: 'v' },
|
||||
map: {
|
||||
rdn: 'u',
|
||||
name: 'v',
|
||||
description: 'd',
|
||||
displayName: 'c',
|
||||
email: 'm',
|
||||
picture: 'p',
|
||||
memberOf: 'm',
|
||||
},
|
||||
},
|
||||
groups: {
|
||||
dn: 'gdn',
|
||||
options: {
|
||||
scope: 'base',
|
||||
attributes: ['*'],
|
||||
filter: 'f',
|
||||
paged: {
|
||||
pageSize: 7,
|
||||
pagePause: true,
|
||||
users: [
|
||||
{
|
||||
dn: 'udn',
|
||||
options: {
|
||||
scope: 'base',
|
||||
attributes: ['*'],
|
||||
filter: 'f',
|
||||
paged: true,
|
||||
timeLimit: 42,
|
||||
sizeLimit: 100,
|
||||
derefAliases: 0,
|
||||
typesOnly: false,
|
||||
},
|
||||
set: { p: 'v' },
|
||||
map: {
|
||||
rdn: 'u',
|
||||
name: 'v',
|
||||
description: 'd',
|
||||
displayName: 'c',
|
||||
email: 'm',
|
||||
picture: 'p',
|
||||
memberOf: 'm',
|
||||
},
|
||||
timeLimit: 42,
|
||||
sizeLimit: 100,
|
||||
derefAliases: 1,
|
||||
typesOnly: true,
|
||||
},
|
||||
set: { p: 'v' },
|
||||
map: {
|
||||
rdn: 'u',
|
||||
name: 'v',
|
||||
description: 'd',
|
||||
type: 't',
|
||||
displayName: 'c',
|
||||
email: 'm',
|
||||
picture: 'p',
|
||||
memberOf: 'm',
|
||||
members: 'n',
|
||||
],
|
||||
groups: [
|
||||
{
|
||||
dn: 'gdn',
|
||||
options: {
|
||||
scope: 'base',
|
||||
attributes: ['*'],
|
||||
filter: 'f',
|
||||
paged: {
|
||||
pageSize: 7,
|
||||
pagePause: true,
|
||||
},
|
||||
timeLimit: 42,
|
||||
sizeLimit: 100,
|
||||
derefAliases: 1,
|
||||
typesOnly: true,
|
||||
},
|
||||
set: { p: 'v' },
|
||||
map: {
|
||||
rdn: 'u',
|
||||
name: 'v',
|
||||
description: 'd',
|
||||
type: 't',
|
||||
displayName: 'c',
|
||||
email: 'm',
|
||||
picture: 'p',
|
||||
memberOf: 'm',
|
||||
members: 'n',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
expect(actual).toEqual(expected);
|
||||
@@ -316,7 +328,7 @@ describe('readLdapConfig', () => {
|
||||
const actual = readProviderConfigs(new ConfigReader(config));
|
||||
|
||||
const expected = '(|(cn=foo bar)(cn=bar))';
|
||||
expect(actual[0].users.options.filter).toEqual(expected);
|
||||
expect(actual[0].users[0].options.filter).toEqual(expected);
|
||||
});
|
||||
|
||||
it('supports a dot nested set structure', () => {
|
||||
@@ -353,7 +365,9 @@ describe('readLdapConfig', () => {
|
||||
};
|
||||
const actual = readProviderConfigs(new ConfigReader(config));
|
||||
|
||||
expect(actual[0].users.set).toEqual({ 'metadata.annotations': { a: 'b' } });
|
||||
expect(actual[0].users[0].set).toEqual({
|
||||
'metadata.annotations': { a: 'b' },
|
||||
});
|
||||
});
|
||||
|
||||
it('throws on attempts to modify the set structure', () => {
|
||||
@@ -389,25 +403,77 @@ describe('readLdapConfig', () => {
|
||||
const actual = readProviderConfigs(new ConfigReader(config));
|
||||
|
||||
expect(() => {
|
||||
(actual[0].users.set as any).y = 2;
|
||||
(actual[0].users[0].set as any).y = 2;
|
||||
}).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Cannot add property y, object is not extensible"`,
|
||||
);
|
||||
expect(() => {
|
||||
(actual[0].users.set as any).x.b = 2;
|
||||
(actual[0].users[0].set as any).x.b = 2;
|
||||
}).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Cannot add property b, object is not extensible"`,
|
||||
);
|
||||
|
||||
expect(() => {
|
||||
(actual[0].groups.set as any).y = 2;
|
||||
(actual[0].groups[0].set as any).y = 2;
|
||||
}).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Cannot add property y, object is not extensible"`,
|
||||
);
|
||||
expect(() => {
|
||||
(actual[0].groups.set as any).x.b = 2;
|
||||
(actual[0].groups[0].set as any).x.b = 2;
|
||||
}).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Cannot add property b, object is not extensible"`,
|
||||
);
|
||||
});
|
||||
|
||||
it('supports users/groups config as list', () => {
|
||||
const config = {
|
||||
catalog: {
|
||||
providers: {
|
||||
ldapOrg: {
|
||||
default: {
|
||||
target: 'target',
|
||||
users: [
|
||||
{
|
||||
dn: 'udn1',
|
||||
},
|
||||
{
|
||||
dn: 'udn2',
|
||||
},
|
||||
],
|
||||
groups: [
|
||||
{
|
||||
dn: 'gdn1',
|
||||
},
|
||||
{
|
||||
dn: 'gdn2',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const actual = readProviderConfigs(new ConfigReader(config));
|
||||
|
||||
expect(actual[0].users).toHaveLength(2);
|
||||
expect(actual[0].groups).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('supports users/groups config as undefined', () => {
|
||||
const config = {
|
||||
catalog: {
|
||||
providers: {
|
||||
ldapOrg: {
|
||||
default: {
|
||||
target: 'target',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const actual = readProviderConfigs(new ConfigReader(config));
|
||||
|
||||
expect(actual[0].users).toHaveLength(0);
|
||||
expect(actual[0].groups).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -42,9 +42,9 @@ export type LdapProviderConfig = {
|
||||
// command is not issued.
|
||||
bind?: BindConfig;
|
||||
// The settings that govern the reading and interpretation of users
|
||||
users: UserConfig;
|
||||
users: UserConfig[];
|
||||
// The settings that govern the reading and interpretation of groups
|
||||
groups: GroupConfig;
|
||||
groups: GroupConfig[];
|
||||
// Schedule configuration for refresh tasks.
|
||||
schedule?: SchedulerServiceTaskScheduleDefinition;
|
||||
};
|
||||
@@ -161,34 +161,33 @@ export type GroupConfig = {
|
||||
};
|
||||
};
|
||||
|
||||
const defaultConfig = {
|
||||
users: {
|
||||
options: {
|
||||
scope: 'one',
|
||||
attributes: ['*', '+'],
|
||||
},
|
||||
map: {
|
||||
rdn: 'uid',
|
||||
name: 'uid',
|
||||
displayName: 'cn',
|
||||
email: 'mail',
|
||||
memberOf: 'memberOf',
|
||||
},
|
||||
const defaultUserConfig = {
|
||||
options: {
|
||||
scope: 'one',
|
||||
attributes: ['*', '+'],
|
||||
},
|
||||
groups: {
|
||||
options: {
|
||||
scope: 'one',
|
||||
attributes: ['*', '+'],
|
||||
},
|
||||
map: {
|
||||
rdn: 'cn',
|
||||
name: 'cn',
|
||||
description: 'description',
|
||||
displayName: 'cn',
|
||||
type: 'groupType',
|
||||
memberOf: 'memberOf',
|
||||
members: 'member',
|
||||
},
|
||||
map: {
|
||||
rdn: 'uid',
|
||||
name: 'uid',
|
||||
displayName: 'cn',
|
||||
email: 'mail',
|
||||
memberOf: 'memberOf',
|
||||
},
|
||||
};
|
||||
|
||||
const defaultGroupConfig = {
|
||||
options: {
|
||||
scope: 'one',
|
||||
attributes: ['*', '+'],
|
||||
},
|
||||
map: {
|
||||
rdn: 'cn',
|
||||
name: 'cn',
|
||||
description: 'description',
|
||||
displayName: 'cn',
|
||||
type: 'groupType',
|
||||
memberOf: 'memberOf',
|
||||
members: 'member',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -272,9 +271,7 @@ function readSetConfig(
|
||||
return c.get();
|
||||
}
|
||||
|
||||
function readUserMapConfig(
|
||||
c: Config | undefined,
|
||||
): Partial<LdapProviderConfig['users']['map']> {
|
||||
function readUserMapConfig(c: Config | undefined): Partial<UserConfig['map']> {
|
||||
if (!c) {
|
||||
return {};
|
||||
}
|
||||
@@ -292,7 +289,7 @@ function readUserMapConfig(
|
||||
|
||||
function readGroupMapConfig(
|
||||
c: Config | undefined,
|
||||
): Partial<LdapProviderConfig['groups']['map']> {
|
||||
): Partial<GroupConfig['map']> {
|
||||
if (!c) {
|
||||
return {};
|
||||
}
|
||||
@@ -311,8 +308,18 @@ function readGroupMapConfig(
|
||||
}
|
||||
|
||||
function readUserConfig(
|
||||
c: Config,
|
||||
c: Config | Config[] | undefined,
|
||||
): RecursivePartial<LdapProviderConfig['users']> {
|
||||
if (!c) {
|
||||
return [];
|
||||
}
|
||||
if (Array.isArray(c)) {
|
||||
return c.map(it => readSingleUserConfig(it));
|
||||
}
|
||||
return [readSingleUserConfig(c)];
|
||||
}
|
||||
|
||||
function readSingleUserConfig(c: Config): RecursivePartial<UserConfig> {
|
||||
return {
|
||||
dn: c.getString('dn'),
|
||||
options: readOptionsConfig(c.getOptionalConfig('options')),
|
||||
@@ -322,8 +329,18 @@ function readUserConfig(
|
||||
}
|
||||
|
||||
function readGroupConfig(
|
||||
c: Config,
|
||||
c: Config | Config[] | undefined,
|
||||
): RecursivePartial<LdapProviderConfig['groups']> {
|
||||
if (!c) {
|
||||
return [];
|
||||
}
|
||||
if (Array.isArray(c)) {
|
||||
return c.map(it => readSingleGroupConfig(it));
|
||||
}
|
||||
return [readSingleGroupConfig(c)];
|
||||
}
|
||||
|
||||
function readSingleGroupConfig(c: Config): RecursivePartial<GroupConfig> {
|
||||
return {
|
||||
dn: c.getString('dn'),
|
||||
options: readOptionsConfig(c.getOptionalConfig('options')),
|
||||
@@ -352,14 +369,15 @@ export function readLdapLegacyConfig(config: Config): LdapProviderConfig[] {
|
||||
target: trimEnd(c.getString('target'), '/'),
|
||||
tls: readTlsConfig(c.getOptionalConfig('tls')),
|
||||
bind: readBindConfig(c.getOptionalConfig('bind')),
|
||||
users: readUserConfig(c.getConfig('users')),
|
||||
groups: readGroupConfig(c.getConfig('groups')),
|
||||
users: readUserConfig(c.getConfig('users')).map(it => {
|
||||
return mergeWith({}, defaultUserConfig, it, replaceArraysIfPresent);
|
||||
}),
|
||||
groups: readGroupConfig(c.getConfig('groups')).map(it => {
|
||||
return mergeWith({}, defaultGroupConfig, it, replaceArraysIfPresent);
|
||||
}),
|
||||
};
|
||||
const merged = mergeWith({}, defaultConfig, newConfig, (_into, from) => {
|
||||
// Replace arrays instead of merging, otherwise default behavior
|
||||
return Array.isArray(from) ? from : undefined;
|
||||
});
|
||||
return freeze(merged) as LdapProviderConfig;
|
||||
|
||||
return freeze(newConfig) as LdapProviderConfig;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -385,19 +403,36 @@ export function readProviderConfigs(config: Config): LdapProviderConfig[] {
|
||||
)
|
||||
: undefined;
|
||||
|
||||
const isUserList = Array.isArray(c.getOptional('users'));
|
||||
const isGroupList = Array.isArray(c.getOptional('groups'));
|
||||
|
||||
const newConfig = {
|
||||
id,
|
||||
target: trimEnd(c.getString('target'), '/'),
|
||||
tls: readTlsConfig(c.getOptionalConfig('tls')),
|
||||
bind: readBindConfig(c.getOptionalConfig('bind')),
|
||||
users: readUserConfig(c.getConfig('users')),
|
||||
groups: readGroupConfig(c.getConfig('groups')),
|
||||
users: readUserConfig(
|
||||
isUserList
|
||||
? c.getOptionalConfigArray('users')
|
||||
: c.getOptionalConfig('users'),
|
||||
).map(it => {
|
||||
return mergeWith({}, defaultUserConfig, it, replaceArraysIfPresent);
|
||||
}),
|
||||
groups: readGroupConfig(
|
||||
isGroupList
|
||||
? c.getOptionalConfigArray('groups')
|
||||
: c.getOptionalConfig('groups'),
|
||||
).map(it => {
|
||||
return mergeWith({}, defaultGroupConfig, it, replaceArraysIfPresent);
|
||||
}),
|
||||
schedule,
|
||||
};
|
||||
const merged = mergeWith({}, defaultConfig, newConfig, (_into, from) => {
|
||||
// Replace arrays instead of merging, otherwise default behavior
|
||||
return Array.isArray(from) ? from : undefined;
|
||||
});
|
||||
return freeze(merged) as LdapProviderConfig;
|
||||
|
||||
return freeze(newConfig) as LdapProviderConfig;
|
||||
});
|
||||
}
|
||||
|
||||
function replaceArraysIfPresent(_into: any, from: any) {
|
||||
// Replace arrays instead of merging, otherwise default behavior
|
||||
return Array.isArray(from) ? from : undefined;
|
||||
}
|
||||
|
||||
@@ -99,19 +99,21 @@ describe('readLdapUsers', () => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
const config: UserConfig = {
|
||||
dn: 'ddd',
|
||||
options: {},
|
||||
map: {
|
||||
rdn: 'uid',
|
||||
name: 'uid',
|
||||
description: 'description',
|
||||
displayName: 'cn',
|
||||
email: 'mail',
|
||||
picture: 'avatarUrl',
|
||||
memberOf: 'memberOf',
|
||||
const config: UserConfig[] = [
|
||||
{
|
||||
dn: 'ddd',
|
||||
options: {},
|
||||
map: {
|
||||
rdn: 'uid',
|
||||
name: 'uid',
|
||||
description: 'description',
|
||||
displayName: 'cn',
|
||||
email: 'mail',
|
||||
picture: 'avatarUrl',
|
||||
memberOf: 'memberOf',
|
||||
},
|
||||
},
|
||||
};
|
||||
];
|
||||
const { users, userMemberOf } = await readLdapUsers(client, config);
|
||||
expect(users).toEqual([
|
||||
expect.objectContaining({
|
||||
@@ -160,19 +162,21 @@ describe('readLdapUsers', () => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
const config: UserConfig = {
|
||||
dn: 'ddd',
|
||||
options: {},
|
||||
map: {
|
||||
rdn: 'uid',
|
||||
name: 'uid',
|
||||
description: 'description',
|
||||
displayName: 'cn',
|
||||
email: 'mail',
|
||||
picture: 'avatarUrl',
|
||||
memberOf: 'memberOf',
|
||||
const config: UserConfig[] = [
|
||||
{
|
||||
dn: 'ddd',
|
||||
options: {},
|
||||
map: {
|
||||
rdn: 'uid',
|
||||
name: 'uid',
|
||||
description: 'description',
|
||||
displayName: 'cn',
|
||||
email: 'mail',
|
||||
picture: 'avatarUrl',
|
||||
memberOf: 'memberOf',
|
||||
},
|
||||
},
|
||||
};
|
||||
];
|
||||
const { users, userMemberOf } = await readLdapUsers(client, config);
|
||||
expect(users).toEqual([
|
||||
expect.objectContaining({
|
||||
@@ -216,19 +220,21 @@ describe('readLdapUsers', () => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
const config: UserConfig = {
|
||||
dn: 'ddd',
|
||||
options: {},
|
||||
map: {
|
||||
rdn: 'uid',
|
||||
name: 'uid',
|
||||
description: 'description',
|
||||
displayName: 'cn',
|
||||
email: 'mail',
|
||||
picture: 'avatarUrl',
|
||||
memberOf: 'memberOf',
|
||||
const config: UserConfig[] = [
|
||||
{
|
||||
dn: 'ddd',
|
||||
options: {},
|
||||
map: {
|
||||
rdn: 'uid',
|
||||
name: 'uid',
|
||||
description: 'description',
|
||||
displayName: 'cn',
|
||||
email: 'mail',
|
||||
picture: 'avatarUrl',
|
||||
memberOf: 'memberOf',
|
||||
},
|
||||
},
|
||||
};
|
||||
];
|
||||
const { users, userMemberOf } = await readLdapUsers(client, config);
|
||||
expect(users).toEqual([
|
||||
expect.objectContaining({
|
||||
@@ -255,6 +261,58 @@ describe('readLdapUsers', () => {
|
||||
new Map([['dn-value', new Set(['x', 'y', 'z'])]]),
|
||||
);
|
||||
});
|
||||
it('can process a list of UserConfigs', async () => {
|
||||
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'],
|
||||
entryDN: ['dn-value'],
|
||||
entryUUID: ['uuid-value'],
|
||||
}),
|
||||
);
|
||||
});
|
||||
const config: UserConfig[] = [
|
||||
{
|
||||
dn: 'ddd',
|
||||
options: {},
|
||||
map: {
|
||||
rdn: 'uid',
|
||||
name: 'uid',
|
||||
description: 'description',
|
||||
displayName: 'cn',
|
||||
email: 'mail',
|
||||
picture: 'avatarUrl',
|
||||
memberOf: 'memberOf',
|
||||
},
|
||||
},
|
||||
{
|
||||
dn: 'ddd',
|
||||
options: {},
|
||||
map: {
|
||||
rdn: 'uid',
|
||||
name: 'uid',
|
||||
description: 'description',
|
||||
displayName: 'cn',
|
||||
email: 'mail',
|
||||
picture: 'avatarUrl',
|
||||
memberOf: 'memberOf',
|
||||
},
|
||||
},
|
||||
];
|
||||
const { users } = await readLdapUsers(client, config);
|
||||
expect(users).toHaveLength(2);
|
||||
});
|
||||
it('can process no UserConfigs', async () => {
|
||||
const config: UserConfig[] = [];
|
||||
const { users } = await readLdapUsers(client, config);
|
||||
expect(users).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('readLdapGroups', () => {
|
||||
@@ -282,21 +340,23 @@ describe('readLdapGroups', () => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
const config: GroupConfig = {
|
||||
dn: 'ddd',
|
||||
options: {},
|
||||
map: {
|
||||
rdn: 'cn',
|
||||
name: 'cn',
|
||||
description: 'description',
|
||||
displayName: 'cn',
|
||||
email: 'mail',
|
||||
picture: 'avatarUrl',
|
||||
type: 'tt',
|
||||
memberOf: 'memberOf',
|
||||
members: 'member',
|
||||
const config: GroupConfig[] = [
|
||||
{
|
||||
dn: 'ddd',
|
||||
options: {},
|
||||
map: {
|
||||
rdn: 'cn',
|
||||
name: 'cn',
|
||||
description: 'description',
|
||||
displayName: 'cn',
|
||||
email: 'mail',
|
||||
picture: 'avatarUrl',
|
||||
type: 'tt',
|
||||
memberOf: 'memberOf',
|
||||
members: 'member',
|
||||
},
|
||||
},
|
||||
};
|
||||
];
|
||||
const { groups, groupMember, groupMemberOf } = await readLdapGroups(
|
||||
client,
|
||||
config,
|
||||
@@ -353,21 +413,23 @@ describe('readLdapGroups', () => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
const config: GroupConfig = {
|
||||
dn: 'ddd',
|
||||
options: {},
|
||||
map: {
|
||||
rdn: 'cn',
|
||||
name: 'cn',
|
||||
description: 'description',
|
||||
displayName: 'cn',
|
||||
email: 'mail',
|
||||
picture: 'avatarUrl',
|
||||
type: 'tt',
|
||||
memberOf: 'memberOf',
|
||||
members: 'member',
|
||||
const config: GroupConfig[] = [
|
||||
{
|
||||
dn: 'ddd',
|
||||
options: {},
|
||||
map: {
|
||||
rdn: 'cn',
|
||||
name: 'cn',
|
||||
description: 'description',
|
||||
displayName: 'cn',
|
||||
email: 'mail',
|
||||
picture: 'avatarUrl',
|
||||
type: 'tt',
|
||||
memberOf: 'memberOf',
|
||||
members: 'member',
|
||||
},
|
||||
},
|
||||
};
|
||||
];
|
||||
const { groups, groupMember, groupMemberOf } = await readLdapGroups(
|
||||
client,
|
||||
config,
|
||||
@@ -401,6 +463,65 @@ describe('readLdapGroups', () => {
|
||||
new Map([['dn-value', new Set(['x', 'y', 'z'])]]),
|
||||
);
|
||||
});
|
||||
|
||||
it('can process a list of GroupConfigs', 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'],
|
||||
entryDN: ['dn-value'],
|
||||
entryUUID: ['uuid-value'],
|
||||
}),
|
||||
);
|
||||
});
|
||||
const config: GroupConfig[] = [
|
||||
{
|
||||
dn: 'ddd',
|
||||
options: {},
|
||||
map: {
|
||||
rdn: 'cn',
|
||||
name: 'cn',
|
||||
description: 'description',
|
||||
displayName: 'cn',
|
||||
email: 'mail',
|
||||
picture: 'avatarUrl',
|
||||
type: 'tt',
|
||||
memberOf: 'memberOf',
|
||||
members: 'member',
|
||||
},
|
||||
},
|
||||
{
|
||||
dn: 'ddd',
|
||||
options: {},
|
||||
map: {
|
||||
rdn: 'cn',
|
||||
name: 'cn',
|
||||
description: 'description',
|
||||
displayName: 'cn',
|
||||
email: 'mail',
|
||||
picture: 'avatarUrl',
|
||||
type: 'tt',
|
||||
memberOf: 'memberOf',
|
||||
members: 'member',
|
||||
},
|
||||
},
|
||||
];
|
||||
const { groups } = await readLdapGroups(client, config);
|
||||
expect(groups).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('can process no GroupConfigs', async () => {
|
||||
const config: GroupConfig[] = [];
|
||||
const { groups } = await readLdapGroups(client, config);
|
||||
expect(groups).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveRelations', () => {
|
||||
|
||||
@@ -104,32 +104,36 @@ export async function defaultUserTransformer(
|
||||
*/
|
||||
export async function readLdapUsers(
|
||||
client: LdapClient,
|
||||
config: UserConfig,
|
||||
config: UserConfig[],
|
||||
opts?: { transformer?: UserTransformer },
|
||||
): Promise<{
|
||||
users: UserEntity[]; // With all relations empty
|
||||
userMemberOf: Map<string, Set<string>>; // DN -> DN or UUID of groups
|
||||
}> {
|
||||
const { dn, options, map } = config;
|
||||
const vendor = await client.getVendor();
|
||||
|
||||
if (config.length === 0) {
|
||||
return { users: [], userMemberOf: new Map() };
|
||||
}
|
||||
const entities: UserEntity[] = [];
|
||||
const userMemberOf: Map<string, Set<string>> = new Map();
|
||||
|
||||
const vendor = await client.getVendor();
|
||||
const transformer = opts?.transformer ?? defaultUserTransformer;
|
||||
|
||||
await client.searchStreaming(dn, options, async user => {
|
||||
const entity = await transformer(vendor, config, user);
|
||||
for (const cfg of config) {
|
||||
const { dn, options, map } = cfg;
|
||||
await client.searchStreaming(dn, options, async user => {
|
||||
const entity = await transformer(vendor, cfg, user);
|
||||
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
|
||||
mapReferencesAttr(user, vendor, map.memberOf, (myDn, vs) => {
|
||||
ensureItems(userMemberOf, myDn, vs);
|
||||
mapReferencesAttr(user, vendor, map.memberOf, (myDn, vs) => {
|
||||
ensureItems(userMemberOf, myDn, vs);
|
||||
});
|
||||
entities.push(entity);
|
||||
});
|
||||
entities.push(entity);
|
||||
});
|
||||
}
|
||||
|
||||
return { users: entities, userMemberOf };
|
||||
}
|
||||
@@ -206,7 +210,7 @@ export async function defaultGroupTransformer(
|
||||
*/
|
||||
export async function readLdapGroups(
|
||||
client: LdapClient,
|
||||
config: GroupConfig,
|
||||
config: GroupConfig[],
|
||||
opts?: {
|
||||
transformer?: GroupTransformer;
|
||||
},
|
||||
@@ -215,35 +219,40 @@ export async function readLdapGroups(
|
||||
groupMemberOf: Map<string, Set<string>>; // DN -> DN or UUID of groups
|
||||
groupMember: Map<string, Set<string>>; // DN -> DN or UUID of groups & users
|
||||
}> {
|
||||
if (config.length === 0) {
|
||||
return { groups: [], groupMemberOf: new Map(), groupMember: new Map() };
|
||||
}
|
||||
const groups: GroupEntity[] = [];
|
||||
const groupMemberOf: Map<string, Set<string>> = new Map();
|
||||
const groupMember: Map<string, Set<string>> = new Map();
|
||||
|
||||
const { dn, map, options } = config;
|
||||
const vendor = await client.getVendor();
|
||||
|
||||
const transformer = opts?.transformer ?? defaultGroupTransformer;
|
||||
|
||||
await client.searchStreaming(dn, options, async entry => {
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
for (const cfg of config) {
|
||||
const { dn, map, options } = cfg;
|
||||
|
||||
const entity = await transformer(vendor, config, entry);
|
||||
await client.searchStreaming(dn, options, async entry => {
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
const entity = await transformer(vendor, cfg, entry);
|
||||
|
||||
mapReferencesAttr(entry, vendor, map.memberOf, (myDn, vs) => {
|
||||
ensureItems(groupMemberOf, myDn, vs);
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
|
||||
mapReferencesAttr(entry, vendor, map.memberOf, (myDn, vs) => {
|
||||
ensureItems(groupMemberOf, myDn, vs);
|
||||
});
|
||||
mapReferencesAttr(entry, vendor, map.members, (myDn, vs) => {
|
||||
ensureItems(groupMember, myDn, vs);
|
||||
});
|
||||
|
||||
groups.push(entity);
|
||||
});
|
||||
mapReferencesAttr(entry, vendor, map.members, (myDn, vs) => {
|
||||
ensureItems(groupMember, myDn, vs);
|
||||
});
|
||||
|
||||
groups.push(entity);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
groups,
|
||||
@@ -264,8 +273,8 @@ export async function readLdapGroups(
|
||||
*/
|
||||
export async function readLdapOrg(
|
||||
client: LdapClient,
|
||||
userConfig: UserConfig,
|
||||
groupConfig: GroupConfig,
|
||||
userConfig: UserConfig[],
|
||||
groupConfig: GroupConfig[],
|
||||
options: {
|
||||
groupTransformer?: GroupTransformer;
|
||||
userTransformer?: UserTransformer;
|
||||
|
||||
Reference in New Issue
Block a user