Merge pull request #6116 from goober/feature/extract-ldap-processor
Extract LdapOrgReaderProcessor into a separate package
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': minor
|
||||
---
|
||||
|
||||
Move `LdapOrgReaderProcessor` from `@backstage/plugin-catalog-backend`
|
||||
to `@backstage/plugin-catalog-backend-module-ldap`.
|
||||
|
||||
The `LdapOrgReaderProcessor` isn't registered by default anymore, if
|
||||
you want to continue using it you have to register it manually at the catalog
|
||||
builder:
|
||||
|
||||
1. Add dependency to `@backstage/plugin-catalog-backend-module-ldap` to the `package.json` of your backend.
|
||||
2. Add the processor to the catalog builder:
|
||||
|
||||
```typescript
|
||||
// packages/backend/src/plugins/catalog.ts
|
||||
builder.addProcessor(
|
||||
LdapOrgReaderProcessor.fromConfig(config, {
|
||||
logger,
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
For more configuration details, see the [README of the `@backstage/plugin-catalog-backend-module-ldap` package](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend-module-ldap/README.md).
|
||||
@@ -122,6 +122,7 @@ Knex
|
||||
kubectl
|
||||
kubernetes
|
||||
kubernetes
|
||||
ldap
|
||||
learnings
|
||||
Leasot
|
||||
lerna
|
||||
|
||||
@@ -14,19 +14,25 @@ entities that mirror your org setup.
|
||||
|
||||
## Installation
|
||||
|
||||
The processor that performs the import, `LdapOrgReaderProcessor`, comes
|
||||
installed with the default setup of Backstage.
|
||||
1. The processor is not installed by default, therefore you have to add a
|
||||
dependency to `@backstage/plugin-catalog-backend-module-ldap` to your backend
|
||||
package.
|
||||
|
||||
If you replace the set of processors in your installation using that facility of
|
||||
the catalog builder class, you can import and add it as follows.
|
||||
```bash
|
||||
# From your Backstage root directory
|
||||
cd packages/backend
|
||||
yarn add @backstage/plugin-catalog-backend-module-ldap
|
||||
```
|
||||
|
||||
```ts
|
||||
// Typically in packages/backend/src/plugins/catalog.ts
|
||||
import { LdapOrgReaderProcessor } from '@backstage/plugin-catalog-backend';
|
||||
2. The `LdapOrgReaderProcessor` is not registered by default, so you have to
|
||||
register it in the catalog plugin:
|
||||
|
||||
builder.replaceProcessors(
|
||||
LdapOrgReaderProcessor.fromConfig(config, { logger }),
|
||||
// ...
|
||||
```typescript
|
||||
// packages/backend/src/plugins/catalog.ts
|
||||
builder.addProcessor(
|
||||
LdapOrgReaderProcessor.fromConfig(config, {
|
||||
logger,
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: [require.resolve('@backstage/cli/config/eslint.backend')],
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
# Catalog Backend Module for LDAP
|
||||
|
||||
This is an extension module to the plugin-catalog-backend plugin, providing an LdapOrgReaderProcessor that can be
|
||||
used to ingest organization data from an LDAP server. This processor is useful if you want to import users and
|
||||
groups from your Active Directory or another LDAP compatible server.
|
||||
|
||||
## Getting started
|
||||
|
||||
See [Backstage documentation](https://backstage.io/docs/integrations/ldap/org) for details on how to install
|
||||
and configure the plugin.
|
||||
@@ -0,0 +1,72 @@
|
||||
## API Report File for "@backstage/plugin-catalog-backend-module-ldap"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
|
||||
import { CatalogProcessor } from '@backstage/plugin-catalog-backend';
|
||||
import { CatalogProcessorEmit } from '@backstage/plugin-catalog-backend';
|
||||
import { Client } from 'ldapjs';
|
||||
import { Config } from '@backstage/config';
|
||||
import { GroupEntity } from '@backstage/catalog-model';
|
||||
import { JsonValue } from '@backstage/config';
|
||||
import { LocationSpec } from '@backstage/catalog-model';
|
||||
import { Logger } from 'winston';
|
||||
import { SearchEntry } from 'ldapjs';
|
||||
import { SearchOptions } from 'ldapjs';
|
||||
import { UserEntity } from '@backstage/catalog-model';
|
||||
|
||||
// @public
|
||||
export const LDAP_DN_ANNOTATION = "backstage.io/ldap-dn";
|
||||
|
||||
// @public
|
||||
export const LDAP_RDN_ANNOTATION = "backstage.io/ldap-rdn";
|
||||
|
||||
// @public
|
||||
export const LDAP_UUID_ANNOTATION = "backstage.io/ldap-uuid";
|
||||
|
||||
// @public
|
||||
export class LdapClient {
|
||||
constructor(client: Client);
|
||||
// (undocumented)
|
||||
static create(logger: Logger, target: string, bind?: BindConfig): Promise<LdapClient>;
|
||||
getRootDSE(): Promise<SearchEntry | undefined>;
|
||||
getVendor(): Promise<LdapVendor>;
|
||||
search(dn: string, options: SearchOptions): Promise<SearchEntry[]>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export class LdapOrgReaderProcessor implements CatalogProcessor {
|
||||
constructor(options: {
|
||||
providers: LdapProviderConfig[];
|
||||
logger: Logger;
|
||||
});
|
||||
// (undocumented)
|
||||
static fromConfig(config: Config, options: {
|
||||
logger: Logger;
|
||||
}): LdapOrgReaderProcessor;
|
||||
// (undocumented)
|
||||
readLocation(location: LocationSpec, _optional: boolean, emit: CatalogProcessorEmit): Promise<boolean>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export type LdapProviderConfig = {
|
||||
target: string;
|
||||
bind?: BindConfig;
|
||||
users: UserConfig;
|
||||
groups: GroupConfig;
|
||||
};
|
||||
|
||||
// @public
|
||||
export function readLdapConfig(config: Config): LdapProviderConfig[];
|
||||
|
||||
// @public
|
||||
export function readLdapOrg(client: LdapClient, userConfig: UserConfig, groupConfig: GroupConfig): Promise<{
|
||||
users: UserEntity[];
|
||||
groups: GroupEntity[];
|
||||
}>;
|
||||
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
|
||||
```
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { JsonValue } from '@backstage/config';
|
||||
|
||||
export interface Config {
|
||||
/**
|
||||
* Configuration options for the catalog plugin.
|
||||
*/
|
||||
catalog?: {
|
||||
/**
|
||||
* List of processor-specific options and attributes
|
||||
*/
|
||||
processors?: {
|
||||
/**
|
||||
* LdapOrgReaderProcessor configuration
|
||||
*/
|
||||
ldapOrg?: {
|
||||
/**
|
||||
* The configuration parameters for each single LDAP provider.
|
||||
*/
|
||||
providers: Array<{
|
||||
/**
|
||||
* The prefix of the target that this matches on, e.g.
|
||||
* "ldaps://ds.example.net", with no trailing slash.
|
||||
*/
|
||||
target: string;
|
||||
|
||||
/**
|
||||
* The settings to use for the bind command. If none are specified,
|
||||
* the bind command is not issued.
|
||||
*/
|
||||
bind?: {
|
||||
/**
|
||||
* The DN of the user to auth as.
|
||||
*
|
||||
* E.g. "uid=ldap-robot,ou=robots,ou=example,dc=example,dc=net"
|
||||
*/
|
||||
dn: string;
|
||||
/**
|
||||
* The secret of the user to auth as (its password).
|
||||
*
|
||||
* @visibility secret
|
||||
*/
|
||||
secret: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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[];
|
||||
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[];
|
||||
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;
|
||||
};
|
||||
};
|
||||
}>;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "@backstage/plugin-catalog-backend-module-ldap",
|
||||
"version": "0.1.0",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.cjs.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"homepage": "https://backstage.io",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/backstage/backstage",
|
||||
"directory": "plugins/catalog-backend-module-ldap"
|
||||
},
|
||||
"keywords": [
|
||||
"backstage"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "backstage-cli backend:build",
|
||||
"lint": "backstage-cli lint",
|
||||
"test": "backstage-cli test",
|
||||
"prepack": "backstage-cli prepack",
|
||||
"postpack": "backstage-cli postpack",
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/catalog-model": "^0.8.3",
|
||||
"@backstage/config": "^0.1.5",
|
||||
"@backstage/plugin-catalog-backend": "^0.10.2",
|
||||
"@types/ldapjs": "^1.0.10",
|
||||
"ldapjs": "^2.2.0",
|
||||
"lodash": "^4.17.15",
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.7.1",
|
||||
"@backstage/test-utils": "^0.1.13",
|
||||
"@types/lodash": "^4.14.151",
|
||||
"msw": "^0.21.2"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"config.d.ts"
|
||||
],
|
||||
"configSchema": "config.d.ts"
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './processors';
|
||||
export * from './ldap';
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
import { Config, JsonValue } from '@backstage/config';
|
||||
import { SearchOptions } from 'ldapjs';
|
||||
import mergeWith from 'lodash/mergeWith';
|
||||
import { RecursivePartial } from '../../../util';
|
||||
import { RecursivePartial } from '@backstage/plugin-catalog-backend';
|
||||
|
||||
/**
|
||||
* The configuration parameters for a single LDAP provider.
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { GroupEntity, UserEntity } from '@backstage/catalog-model';
|
||||
import { buildMemberOf, buildOrgHierarchy } from './org';
|
||||
|
||||
function g(
|
||||
name: string,
|
||||
parent: string | undefined,
|
||||
children: string[],
|
||||
): GroupEntity {
|
||||
return {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Group',
|
||||
metadata: { name },
|
||||
spec: { type: 'team', parent, children },
|
||||
};
|
||||
}
|
||||
|
||||
describe('buildOrgHierarchy', () => {
|
||||
it('adds groups to their parent.children', () => {
|
||||
const a = g('a', undefined, []);
|
||||
const b = g('b', 'a', []);
|
||||
const c = g('c', 'b', []);
|
||||
const d = g('d', 'a', []);
|
||||
buildOrgHierarchy([a, b, c, d]);
|
||||
expect(a.spec.children).toEqual(expect.arrayContaining(['b', 'd']));
|
||||
expect(b.spec.children).toEqual(expect.arrayContaining(['c']));
|
||||
expect(c.spec.children).toEqual([]);
|
||||
expect(d.spec.children).toEqual([]);
|
||||
});
|
||||
|
||||
it('sets parent of groups children', () => {
|
||||
const a = g('a', undefined, ['b', 'd']);
|
||||
const b = g('b', undefined, ['c']);
|
||||
const c = g('c', undefined, []);
|
||||
const d = g('d', undefined, []);
|
||||
buildOrgHierarchy([a, b, c, d]);
|
||||
expect(a.spec.parent).toBeUndefined();
|
||||
expect(b.spec.parent).toBe('a');
|
||||
expect(c.spec.parent).toBe('b');
|
||||
expect(d.spec.parent).toBe('a');
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildMemberOf', () => {
|
||||
it('fills indirect member of groups', () => {
|
||||
const a = g('a', undefined, []);
|
||||
const b = g('b', 'a', []);
|
||||
const c = g('c', 'b', []);
|
||||
const u: UserEntity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'User',
|
||||
metadata: { name: 'n' },
|
||||
spec: { profile: {}, memberOf: ['c'] },
|
||||
};
|
||||
|
||||
const groups = [a, b, c];
|
||||
buildOrgHierarchy(groups);
|
||||
buildMemberOf(groups, [u]);
|
||||
expect(u.spec.memberOf).toEqual(expect.arrayContaining(['a', 'b', 'c']));
|
||||
});
|
||||
|
||||
it('takes group spec.members into account', () => {
|
||||
const a = g('a', undefined, []);
|
||||
const b = g('b', 'a', []);
|
||||
const c = g('c', 'b', []);
|
||||
c.spec.members = ['n'];
|
||||
const u: UserEntity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'User',
|
||||
metadata: { name: 'n' },
|
||||
spec: { profile: {}, memberOf: [] },
|
||||
};
|
||||
|
||||
const groups = [a, b, c];
|
||||
buildOrgHierarchy(groups);
|
||||
buildMemberOf(groups, [u]);
|
||||
expect(u.spec.memberOf).toEqual(expect.arrayContaining(['a', 'b', 'c']));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { GroupEntity, UserEntity } from '@backstage/catalog-model';
|
||||
|
||||
// TODO: Copied from plugin-catalog-backend, but we could also export them from
|
||||
// there. Or move them to catalog-model.
|
||||
|
||||
export function buildOrgHierarchy(groups: GroupEntity[]) {
|
||||
const groupsByName = new Map(groups.map(g => [g.metadata.name, g]));
|
||||
|
||||
//
|
||||
// Make sure that g.parent.children contain g
|
||||
//
|
||||
|
||||
for (const group of groups) {
|
||||
const selfName = group.metadata.name;
|
||||
const parentName = group.spec.parent;
|
||||
if (parentName) {
|
||||
const parent = groupsByName.get(parentName);
|
||||
if (parent && !parent.spec.children.includes(selfName)) {
|
||||
parent.spec.children.push(selfName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Make sure that g.children.parent is g
|
||||
//
|
||||
|
||||
for (const group of groups) {
|
||||
const selfName = group.metadata.name;
|
||||
for (const childName of group.spec.children) {
|
||||
const child = groupsByName.get(childName);
|
||||
if (child && !child.spec.parent) {
|
||||
child.spec.parent = selfName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that users have their transitive group memberships. Requires that
|
||||
// the groups were previously processed with buildOrgHierarchy()
|
||||
export function buildMemberOf(groups: GroupEntity[], users: UserEntity[]) {
|
||||
const groupsByName = new Map(groups.map(g => [g.metadata.name, g]));
|
||||
|
||||
users.forEach(user => {
|
||||
const transitiveMemberOf = new Set<string>();
|
||||
|
||||
const todo = [
|
||||
...user.spec.memberOf,
|
||||
...groups
|
||||
.filter(g => g.spec.members?.includes(user.metadata.name))
|
||||
.map(g => g.metadata.name),
|
||||
];
|
||||
|
||||
for (;;) {
|
||||
const current = todo.pop();
|
||||
if (!current) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!transitiveMemberOf.has(current)) {
|
||||
transitiveMemberOf.add(current);
|
||||
const group = groupsByName.get(current);
|
||||
if (group?.spec.parent) {
|
||||
todo.push(group.spec.parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
user.spec.memberOf = [...transitiveMemberOf];
|
||||
});
|
||||
}
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
import { GroupEntity, UserEntity } from '@backstage/catalog-model';
|
||||
import { SearchEntry } from 'ldapjs';
|
||||
import merge from 'lodash/merge';
|
||||
import { RecursivePartial } from '../../../util';
|
||||
import { RecursivePartial } from '@backstage/plugin-catalog-backend';
|
||||
import { LdapClient } from './client';
|
||||
import { GroupConfig, UserConfig } from './config';
|
||||
import {
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
import { GroupEntity, UserEntity } from '@backstage/catalog-model';
|
||||
import { SearchEntry } from 'ldapjs';
|
||||
import lodashSet from 'lodash/set';
|
||||
import { buildOrgHierarchy } from '../util/org';
|
||||
import { buildOrgHierarchy } from './org';
|
||||
import { LdapClient } from './client';
|
||||
import { GroupConfig, UserConfig } from './config';
|
||||
import {
|
||||
+6
-3
@@ -22,9 +22,12 @@ import {
|
||||
LdapProviderConfig,
|
||||
readLdapConfig,
|
||||
readLdapOrg,
|
||||
} from './ldap';
|
||||
import * as results from './results';
|
||||
import { CatalogProcessor, CatalogProcessorEmit } from './types';
|
||||
} from '../ldap';
|
||||
import {
|
||||
CatalogProcessor,
|
||||
CatalogProcessorEmit,
|
||||
results,
|
||||
} from '@backstage/plugin-catalog-backend';
|
||||
|
||||
/**
|
||||
* Extracts teams and users out of an LDAP server.
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { LdapOrgReaderProcessor } from './LdapOrgReaderProcessor';
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export {};
|
||||
Vendored
-205
@@ -145,211 +145,6 @@ export interface Config {
|
||||
}>;
|
||||
};
|
||||
|
||||
/**
|
||||
* LdapOrgReaderProcessor configuration
|
||||
*/
|
||||
ldapOrg?: {
|
||||
/**
|
||||
* The configuration parameters for each single LDAP provider.
|
||||
*/
|
||||
providers: Array<{
|
||||
/**
|
||||
* The prefix of the target that this matches on, e.g.
|
||||
* "ldaps://ds.example.net", with no trailing slash.
|
||||
*/
|
||||
target: string;
|
||||
|
||||
/**
|
||||
* The settings to use for the bind command. If none are specified,
|
||||
* the bind command is not issued.
|
||||
*/
|
||||
bind?: {
|
||||
/**
|
||||
* The DN of the user to auth as.
|
||||
*
|
||||
* E.g. "uid=ldap-robot,ou=robots,ou=example,dc=example,dc=net"
|
||||
*/
|
||||
dn: string;
|
||||
/**
|
||||
* The secret of the user to auth as (its password).
|
||||
*
|
||||
* @visibility secret
|
||||
*/
|
||||
secret: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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[];
|
||||
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[];
|
||||
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;
|
||||
};
|
||||
};
|
||||
}>;
|
||||
};
|
||||
|
||||
/**
|
||||
* AwsOrganizationCloudAccountProcessor configuration
|
||||
*/
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
"@backstage/search-common": "^0.1.2",
|
||||
"@octokit/graphql": "^4.5.8",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/ldapjs": "^1.0.9",
|
||||
"aws-sdk": "^2.840.0",
|
||||
"codeowners-utils": "^1.0.2",
|
||||
"core-js": "^3.6.5",
|
||||
@@ -51,7 +50,6 @@
|
||||
"git-url-parse": "^11.4.4",
|
||||
"glob": "^7.1.6",
|
||||
"knex": "^0.95.1",
|
||||
"ldapjs": "^2.2.0",
|
||||
"lodash": "^4.17.15",
|
||||
"morgan": "^1.10.0",
|
||||
"p-limit": "^3.0.2",
|
||||
|
||||
@@ -25,7 +25,6 @@ export { CodeOwnersProcessor } from './CodeOwnersProcessor';
|
||||
export { FileReaderProcessor } from './FileReaderProcessor';
|
||||
export { GithubDiscoveryProcessor } from './GithubDiscoveryProcessor';
|
||||
export { GithubOrgReaderProcessor } from './GithubOrgReaderProcessor';
|
||||
export { LdapOrgReaderProcessor } from './LdapOrgReaderProcessor';
|
||||
export { LocationEntityProcessor } from './LocationEntityProcessor';
|
||||
export { PlaceholderProcessor } from './PlaceholderProcessor';
|
||||
export type { PlaceholderResolver } from './PlaceholderProcessor';
|
||||
|
||||
@@ -49,7 +49,6 @@ import {
|
||||
FileReaderProcessor,
|
||||
GithubDiscoveryProcessor,
|
||||
GithubOrgReaderProcessor,
|
||||
LdapOrgReaderProcessor,
|
||||
PlaceholderProcessor,
|
||||
PlaceholderResolver,
|
||||
UrlReaderProcessor,
|
||||
@@ -371,7 +370,6 @@ export class NextCatalogBuilder {
|
||||
BitbucketDiscoveryProcessor.fromConfig(config, { logger }),
|
||||
GithubDiscoveryProcessor.fromConfig(config, { logger }),
|
||||
GithubOrgReaderProcessor.fromConfig(config, { logger }),
|
||||
LdapOrgReaderProcessor.fromConfig(config, { logger }),
|
||||
new UrlReaderProcessor({ reader, logger }),
|
||||
CodeOwnersProcessor.fromConfig(config, { logger, reader }),
|
||||
new AnnotateLocationEntityProcessor({ integrations }),
|
||||
|
||||
@@ -48,7 +48,6 @@ import {
|
||||
GithubOrgReaderProcessor,
|
||||
HigherOrderOperation,
|
||||
HigherOrderOperations,
|
||||
LdapOrgReaderProcessor,
|
||||
LocationEntityProcessor,
|
||||
LocationReaders,
|
||||
PlaceholderProcessor,
|
||||
@@ -317,7 +316,6 @@ export class CatalogBuilder {
|
||||
BitbucketDiscoveryProcessor.fromConfig(config, { logger }),
|
||||
GithubDiscoveryProcessor.fromConfig(config, { logger }),
|
||||
GithubOrgReaderProcessor.fromConfig(config, { logger }),
|
||||
LdapOrgReaderProcessor.fromConfig(config, { logger }),
|
||||
new UrlReaderProcessor({ reader, logger }),
|
||||
CodeOwnersProcessor.fromConfig(config, { logger, reader }),
|
||||
new LocationEntityProcessor({ integrations }),
|
||||
|
||||
@@ -6106,7 +6106,7 @@
|
||||
"@types/koa-compose" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/ldapjs@^1.0.9":
|
||||
"@types/ldapjs@^1.0.10":
|
||||
version "1.0.10"
|
||||
resolved "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-1.0.10.tgz#bac705c9e154b97d69496b5213cc28dbe9715a37"
|
||||
integrity sha512-AMkMxkK/wjYtWebNH2O+rARfo7scBpW3T23g6zmGCwDgbyDbR79XWpcSqhPWdU+fChaF+I3dVyl9X2dT1CyI9w==
|
||||
|
||||
Reference in New Issue
Block a user