catalog: remove group ancestors and descendants
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
---
|
||||
'@backstage/catalog-model': minor
|
||||
'@backstage/plugin-catalog-backend': minor
|
||||
---
|
||||
|
||||
Remove the deprecated fields `ancestors` and `descendants` from the `Group` entity.
|
||||
|
||||
See https://github.com/backstage/backstage/issues/3049 and the PRs linked from it for details.
|
||||
@@ -723,9 +723,7 @@ metadata:
|
||||
spec:
|
||||
type: business-unit
|
||||
parent: ops
|
||||
ancestors: [ops, global-synergies, acme-corp]
|
||||
children: [backstage, other]
|
||||
descendants: [backstage, other, team-a, team-b, team-c, team-d]
|
||||
```
|
||||
|
||||
In addition to the [common envelope metadata](#common-to-all-kinds-the-metadata)
|
||||
@@ -761,25 +759,6 @@ namespace as the user. Only `Group` entities may be referenced. Most commonly,
|
||||
this field points to a group in the same namespace, so in those cases it is
|
||||
sufficient to enter only the `metadata.name` field of that group.
|
||||
|
||||
### `spec.ancestors` [required]
|
||||
|
||||
**NOTE**: This field was marked for deprecation on Nov 22nd, 2020. It will be
|
||||
removed entirely from the model on Dec 6th, 2020 in the repository and will not
|
||||
be present in released packages following the next release after that. Please
|
||||
update your code to not consume this field before the removal date.
|
||||
|
||||
The recursive list of parents up the hierarchy, by stepping through parents one
|
||||
by one. The list must be present, but may be empty if `parent` is not present.
|
||||
The first entry in the list is equal to `parent`, and then the following ones
|
||||
are progressively farther up the hierarchy.
|
||||
|
||||
The entries of this array are
|
||||
[entity references](https://backstage.io/docs/features/software-catalog/references),
|
||||
with the default kind `Group` and the default namespace equal to the same
|
||||
namespace as the user. Only `Group` entities may be referenced. Most commonly,
|
||||
these entries point to groups in the same namespace, so in those cases it is
|
||||
sufficient to enter only the `metadata.name` field of those groups.
|
||||
|
||||
### `spec.children` [required]
|
||||
|
||||
The immediate child groups of this group in the hierarchy (whose `parent` field
|
||||
@@ -794,25 +773,6 @@ namespace as the user. Only `Group` entities may be referenced. Most commonly,
|
||||
these entries point to groups in the same namespace, so in those cases it is
|
||||
sufficient to enter only the `metadata.name` field of those groups.
|
||||
|
||||
### `spec.descendants` [required]
|
||||
|
||||
**NOTE**: This field was marked for deprecation on Nov 22nd, 2020. It will be
|
||||
removed entirely from the model on Dec 6th, 2020 in the repository and will not
|
||||
be present in released packages following the next release after that. Please
|
||||
update your code to not consume this field before the removal date.
|
||||
|
||||
The immediate and recursive child groups of this group in the hierarchy
|
||||
(children, and children's children, etc.). The list must be present, but may be
|
||||
empty if there are no child groups. The items are not guaranteed to be ordered
|
||||
in any particular way.
|
||||
|
||||
The entries of this array are
|
||||
[entity references](https://backstage.io/docs/features/software-catalog/references),
|
||||
with the default kind `Group` and the default namespace equal to the same
|
||||
namespace as the user. Only `Group` entities may be referenced. Most commonly,
|
||||
these entries point to groups in the same namespace, so in those cases it is
|
||||
sufficient to enter only the `metadata.name` field of those groups.
|
||||
|
||||
## Kind: User
|
||||
|
||||
Describes the following entity kind:
|
||||
|
||||
@@ -6,6 +6,4 @@ metadata:
|
||||
spec:
|
||||
type: sub-department
|
||||
parent: infrastructure
|
||||
ancestors: [infrastructure, acme-corp]
|
||||
children: [team-a, team-b]
|
||||
descendants: [team-a, team-b]
|
||||
|
||||
@@ -6,6 +6,4 @@ metadata:
|
||||
spec:
|
||||
type: sub-department
|
||||
parent: infrastructure
|
||||
ancestors: [infrastructure, acme-corp]
|
||||
children: [team-c, team-d]
|
||||
descendants: [team-c, team-d]
|
||||
|
||||
@@ -6,6 +6,4 @@ metadata:
|
||||
spec:
|
||||
type: department
|
||||
parent: acme-corp
|
||||
ancestors: [acme-corp]
|
||||
children: [backstage, boxoffice]
|
||||
descendants: [backstage, boxoffice, team-a, team-b, team-c, team-d]
|
||||
|
||||
@@ -5,10 +5,7 @@ metadata:
|
||||
description: The acme-corp organization
|
||||
spec:
|
||||
type: organization
|
||||
ancestors: []
|
||||
children: [infrastructure]
|
||||
descendants:
|
||||
[infrastructure, backstage, boxoffice, team-a, team-b, team-c, team-d]
|
||||
---
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Location
|
||||
|
||||
@@ -6,9 +6,7 @@ metadata:
|
||||
spec:
|
||||
type: team
|
||||
parent: backstage
|
||||
ancestors: [backstage, infrastructure, acme-corp]
|
||||
children: []
|
||||
descendants: []
|
||||
---
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: User
|
||||
|
||||
@@ -6,9 +6,7 @@ metadata:
|
||||
spec:
|
||||
type: team
|
||||
parent: backstage
|
||||
ancestors: [backstage, infrastructure, acme-corp]
|
||||
children: []
|
||||
descendants: []
|
||||
---
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: User
|
||||
|
||||
@@ -6,9 +6,7 @@ metadata:
|
||||
spec:
|
||||
type: team
|
||||
parent: boxoffice
|
||||
ancestors: [boxoffice, infrastructure, acme-corp]
|
||||
children: []
|
||||
descendants: []
|
||||
---
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: User
|
||||
|
||||
@@ -6,9 +6,7 @@ metadata:
|
||||
spec:
|
||||
type: team
|
||||
parent: boxoffice
|
||||
ancestors: [boxoffice, infrastructure, acme-corp]
|
||||
children: []
|
||||
descendants: []
|
||||
---
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: User
|
||||
|
||||
@@ -34,9 +34,7 @@ describe('GroupV1alpha1Validator', () => {
|
||||
spec: {
|
||||
type: 'squad',
|
||||
parent: 'group-a',
|
||||
ancestors: ['group-a', 'global-synergies', 'acme-corp'],
|
||||
children: ['child-a', 'child-b'],
|
||||
descendants: ['desc-a', 'desc-b'],
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -85,26 +83,6 @@ describe('GroupV1alpha1Validator', () => {
|
||||
await expect(validator.check(entity)).rejects.toThrow(/parent/);
|
||||
});
|
||||
|
||||
it('rejects missing ancestors', async () => {
|
||||
delete (entity as any).spec.ancestors;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/ancestor/);
|
||||
});
|
||||
|
||||
it('rejects empty ancestors', async () => {
|
||||
(entity as any).spec.ancestors = [''];
|
||||
await expect(validator.check(entity)).rejects.toThrow(/ancestor/);
|
||||
});
|
||||
|
||||
it('rejects undefined ancestors', async () => {
|
||||
(entity as any).spec.ancestors = [undefined];
|
||||
await expect(validator.check(entity)).rejects.toThrow(/ancestor/);
|
||||
});
|
||||
|
||||
it('accepts no ancestors', async () => {
|
||||
(entity as any).spec.ancestors = [];
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('rejects missing children', async () => {
|
||||
delete (entity as any).spec.children;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/children/);
|
||||
@@ -124,24 +102,4 @@ describe('GroupV1alpha1Validator', () => {
|
||||
(entity as any).spec.children = [];
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('rejects missing descendants', async () => {
|
||||
delete (entity as any).spec.descendants;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/descendants/);
|
||||
});
|
||||
|
||||
it('rejects empty descendants', async () => {
|
||||
(entity as any).spec.descendants = [''];
|
||||
await expect(validator.check(entity)).rejects.toThrow(/descendants/);
|
||||
});
|
||||
|
||||
it('rejects undefined descendants', async () => {
|
||||
(entity as any).spec.descendants = [undefined];
|
||||
await expect(validator.check(entity)).rejects.toThrow(/descendants/);
|
||||
});
|
||||
|
||||
it('accepts no descendants', async () => {
|
||||
(entity as any).spec.descendants = [];
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -32,21 +32,11 @@ const schema = yup.object<Partial<GroupEntityV1alpha1>>({
|
||||
// one element and there is no simple workaround -_-
|
||||
// the cast is there to convince typescript that the array itself is
|
||||
// required without using .required()
|
||||
ancestors: yup.array(yup.string().required()).test({
|
||||
name: 'isDefined',
|
||||
message: 'ancestors must be defined',
|
||||
test: v => Boolean(v),
|
||||
}) as yup.ArraySchema<string, object>,
|
||||
children: yup.array(yup.string().required()).test({
|
||||
name: 'isDefined',
|
||||
message: 'children must be defined',
|
||||
test: v => Boolean(v),
|
||||
}) as yup.ArraySchema<string, object>,
|
||||
descendants: yup.array(yup.string().required()).test({
|
||||
name: 'isDefined',
|
||||
message: 'descendants must be defined',
|
||||
test: v => Boolean(v),
|
||||
}) as yup.ArraySchema<string, object>,
|
||||
})
|
||||
.required(),
|
||||
});
|
||||
@@ -57,23 +47,7 @@ export interface GroupEntityV1alpha1 extends Entity {
|
||||
spec: {
|
||||
type: string;
|
||||
parent?: string;
|
||||
/**
|
||||
* @deprecated This field will disappear on Dec 6th, 2020. Please remove
|
||||
* any consuming code. Producers can stop producing this field
|
||||
* before that date, as long as the catalog backend uses the
|
||||
* BuiltinKindsEntityProcessor which inserts the fields in the
|
||||
* mean time.
|
||||
*/
|
||||
ancestors: string[];
|
||||
children: string[];
|
||||
/**
|
||||
* @deprecated This field will disappear on Dec 6th, 2020. Please remove
|
||||
* any consuming code. Producers can stop producing this field
|
||||
* before that date, as long as the catalog backend uses the
|
||||
* BuiltinKindsEntityProcessor which inserts the fields in the
|
||||
* mean time.
|
||||
*/
|
||||
descendants: string[];
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -23,34 +23,6 @@ import {
|
||||
import { BuiltinKindsEntityProcessor } from './BuiltinKindsEntityProcessor';
|
||||
|
||||
describe('BuiltinKindsEntityProcessor', () => {
|
||||
it('fills in fields for #3049', async () => {
|
||||
const p = new BuiltinKindsEntityProcessor();
|
||||
const result = await p.preProcessEntity({
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Group',
|
||||
metadata: {
|
||||
name: 'n',
|
||||
},
|
||||
spec: {
|
||||
type: 't',
|
||||
children: [],
|
||||
} as any,
|
||||
});
|
||||
expect(result).toEqual({
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Group',
|
||||
metadata: {
|
||||
name: 'n',
|
||||
},
|
||||
spec: {
|
||||
type: 't',
|
||||
children: [],
|
||||
ancestors: [],
|
||||
descendants: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
describe('postProcessEntity', () => {
|
||||
const processor = new BuiltinKindsEntityProcessor();
|
||||
const location = { type: 'a', target: 'b' };
|
||||
@@ -215,9 +187,7 @@ describe('BuiltinKindsEntityProcessor', () => {
|
||||
spec: {
|
||||
type: 't',
|
||||
parent: 'p',
|
||||
ancestors: [],
|
||||
children: ['c'],
|
||||
descendants: [],
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -53,25 +53,6 @@ export class BuiltinKindsEntityProcessor implements CatalogProcessor {
|
||||
userEntityV1alpha1Validator,
|
||||
];
|
||||
|
||||
async preProcessEntity(entity: Entity): Promise<Entity> {
|
||||
// NOTE(freben): Part of Group field deprecation on Nov 22nd, 2020. Fields
|
||||
// scheduled for removal Dec 6th, 2020. This code can be deleted after that
|
||||
// point. See https://github.com/backstage/backstage/issues/3049
|
||||
if (
|
||||
entity.apiVersion === 'backstage.io/v1alpha1' &&
|
||||
entity.kind === 'Group' &&
|
||||
entity.spec
|
||||
) {
|
||||
if (!entity.spec.ancestors) {
|
||||
entity.spec.ancestors = [];
|
||||
}
|
||||
if (!entity.spec.descendants) {
|
||||
entity.spec.descendants = [];
|
||||
}
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
async validateEntityKind(entity: Entity): Promise<boolean> {
|
||||
for (const validator of this.validators) {
|
||||
const result = await validator.check(entity);
|
||||
|
||||
@@ -47,7 +47,7 @@ function group(data: RecursivePartial<GroupEntity>): GroupEntity {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Group',
|
||||
metadata: { name: 'name' },
|
||||
spec: { type: 'type', ancestors: [], children: [], descendants: [] },
|
||||
spec: { type: 'type', children: [] },
|
||||
} as GroupEntity,
|
||||
data,
|
||||
);
|
||||
@@ -173,9 +173,7 @@ describe('readLdapGroups', () => {
|
||||
},
|
||||
spec: {
|
||||
type: 'type-value',
|
||||
ancestors: [],
|
||||
children: [],
|
||||
descendants: [],
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
@@ -150,9 +150,7 @@ export async function readLdapGroups(
|
||||
},
|
||||
spec: {
|
||||
type: 'unknown',
|
||||
ancestors: [],
|
||||
children: [],
|
||||
descendants: [],
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -49,9 +49,7 @@ function group(data: RecursivePartial<GroupEntity>): GroupEntity {
|
||||
name: 'name',
|
||||
},
|
||||
spec: {
|
||||
ancestors: [],
|
||||
children: [],
|
||||
descendants: [],
|
||||
type: 'team',
|
||||
},
|
||||
} as GroupEntity,
|
||||
@@ -306,33 +304,20 @@ describe('read microsoft graph', () => {
|
||||
resolveRelations(rootGroup, groups, users, groupMember, groupMemberOf);
|
||||
|
||||
expect(rootGroup.spec.parent).toBeUndefined();
|
||||
expect(rootGroup.spec.ancestors).toEqual(expect.arrayContaining([]));
|
||||
expect(rootGroup.spec.children).toEqual(
|
||||
expect.arrayContaining(['a', 'b']),
|
||||
);
|
||||
expect(rootGroup.spec.descendants).toEqual(
|
||||
expect.arrayContaining(['a', 'b', 'c']),
|
||||
);
|
||||
|
||||
expect(groupA.spec.parent).toEqual('root');
|
||||
expect(groupA.spec.ancestors).toEqual(expect.arrayContaining(['root']));
|
||||
expect(groupA.spec.children).toEqual(expect.arrayContaining([]));
|
||||
expect(groupA.spec.descendants).toEqual(expect.arrayContaining([]));
|
||||
|
||||
expect(groupB.spec.parent).toEqual('root');
|
||||
expect(groupB.spec.ancestors).toEqual(expect.arrayContaining(['root']));
|
||||
expect(groupB.spec.children).toEqual(expect.arrayContaining(['c']));
|
||||
expect(groupB.spec.descendants).toEqual(expect.arrayContaining(['c']));
|
||||
|
||||
expect(groupC.spec.parent).toEqual('b');
|
||||
expect(groupC.spec.ancestors).toEqual(
|
||||
expect.arrayContaining(['root', 'b']),
|
||||
);
|
||||
expect(groupC.spec.children).toEqual(expect.arrayContaining([]));
|
||||
expect(groupC.spec.descendants).toEqual(expect.arrayContaining([]));
|
||||
|
||||
expect(user1.spec.memberOf).toEqual(expect.arrayContaining(['a']));
|
||||
|
||||
expect(user2.spec.memberOf).toEqual(expect.arrayContaining(['b', 'c']));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { GroupEntity, UserEntity } from '@backstage/catalog-model';
|
||||
import limiterFactory from 'p-limit';
|
||||
import { buildMemberOf, buildOrgHierarchy } from '../util/org';
|
||||
import { MicrosoftGraphClient } from './client';
|
||||
import {
|
||||
@@ -21,7 +22,6 @@ import {
|
||||
MICROSOFT_GRAPH_TENANT_ID_ANNOTATION,
|
||||
MICROSOFT_GRAPH_USER_ID_ANNOTATION,
|
||||
} from './constants';
|
||||
import limiterFactory from 'p-limit';
|
||||
|
||||
export function normalizeEntityName(name: string): string {
|
||||
return name
|
||||
@@ -98,7 +98,7 @@ export async function readMicrosoftGraphOrganization(
|
||||
): Promise<{
|
||||
rootGroup: GroupEntity; // With all relations empty
|
||||
}> {
|
||||
// For now we expect a single root orgranization
|
||||
// For now we expect a single root organization
|
||||
const organization = await client.getOrganization(tenantId);
|
||||
const name = normalizeEntityName(organization.displayName!);
|
||||
const rootGroup: GroupEntity = {
|
||||
@@ -113,9 +113,7 @@ export async function readMicrosoftGraphOrganization(
|
||||
},
|
||||
spec: {
|
||||
type: 'root',
|
||||
ancestors: [],
|
||||
children: [],
|
||||
descendants: [],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -165,9 +163,7 @@ export async function readMicrosoftGraphGroups(
|
||||
spec: {
|
||||
type: 'team',
|
||||
// TODO: We could include a group email and picture
|
||||
ancestors: [],
|
||||
children: [],
|
||||
descendants: [],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -278,7 +274,7 @@ export function resolveRelations(
|
||||
});
|
||||
});
|
||||
|
||||
// Make sure that all groups have proper ancestors and descendants
|
||||
// Make sure that all groups have proper parents and children
|
||||
buildOrgHierarchy(groups);
|
||||
|
||||
// Set relations for all users
|
||||
|
||||
@@ -100,9 +100,7 @@ describe('github', () => {
|
||||
spec: {
|
||||
type: 'team',
|
||||
parent: 'parent',
|
||||
ancestors: [],
|
||||
children: [],
|
||||
descendants: [],
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -162,9 +162,7 @@ export async function getOrganizationTeams(
|
||||
},
|
||||
spec: {
|
||||
type: 'team',
|
||||
ancestors: [],
|
||||
children: [],
|
||||
descendants: [],
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ function g(
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Group',
|
||||
metadata: { name },
|
||||
spec: { type: 'team', parent, children, ancestors: [], descendants: [] },
|
||||
spec: { type: 'team', parent, children },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -43,28 +43,16 @@ describe('buildOrgHierarchy', () => {
|
||||
expect(d.spec.children).toEqual([]);
|
||||
});
|
||||
|
||||
it('fills out descendants', () => {
|
||||
const a = g('a', undefined, []);
|
||||
const b = g('b', 'a', []);
|
||||
const c = g('c', 'b', []);
|
||||
const d = g('d', 'a', []);
|
||||
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.descendants).toEqual(expect.arrayContaining(['b', 'c', 'd']));
|
||||
expect(b.spec.descendants).toEqual(expect.arrayContaining(['c']));
|
||||
expect(c.spec.descendants).toEqual([]);
|
||||
expect(d.spec.descendants).toEqual([]);
|
||||
});
|
||||
|
||||
it('fills out ancestors', () => {
|
||||
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.ancestors).toEqual([]);
|
||||
expect(b.spec.ancestors).toEqual(expect.arrayContaining(['a']));
|
||||
expect(c.spec.ancestors).toEqual(expect.arrayContaining(['a', 'b']));
|
||||
expect(d.spec.ancestors).toEqual(expect.arrayContaining(['a']));
|
||||
expect(a.spec.parent).toBeUndefined();
|
||||
expect(b.spec.parent).toBe('a');
|
||||
expect(c.spec.parent).toBe('b');
|
||||
expect(d.spec.parent).toBe('a');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -76,7 +64,7 @@ describe('buildMemberOf', () => {
|
||||
const u: UserEntity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'User',
|
||||
metadata: { name },
|
||||
metadata: { name: 'n' },
|
||||
spec: { profile: {}, memberOf: ['c'] },
|
||||
};
|
||||
|
||||
|
||||
@@ -35,62 +35,17 @@ export function buildOrgHierarchy(groups: GroupEntity[]) {
|
||||
}
|
||||
|
||||
//
|
||||
// Make sure that g.descendants is complete
|
||||
// Make sure that g.children.parent is g
|
||||
//
|
||||
|
||||
function visitDescendants(current: GroupEntity): string[] {
|
||||
if (current.spec.descendants.length) {
|
||||
return current.spec.descendants;
|
||||
}
|
||||
|
||||
const accumulator = new Set<string>();
|
||||
for (const childName of current.spec.children) {
|
||||
accumulator.add(childName);
|
||||
for (const group of groups) {
|
||||
const selfName = group.metadata.name;
|
||||
for (const childName of group.spec.children) {
|
||||
const child = groupsByName.get(childName);
|
||||
if (child) {
|
||||
for (const d of visitDescendants(child)) {
|
||||
accumulator.add(d);
|
||||
}
|
||||
if (child && !child.spec.parent) {
|
||||
child.spec.parent = selfName;
|
||||
}
|
||||
}
|
||||
|
||||
const descendants = Array.from(accumulator);
|
||||
current.spec.descendants = descendants;
|
||||
return descendants;
|
||||
}
|
||||
|
||||
for (const group of groups) {
|
||||
visitDescendants(group);
|
||||
}
|
||||
|
||||
//
|
||||
// Make sure that g.ancestors is complete
|
||||
//
|
||||
|
||||
function visitAncestors(current: GroupEntity): string[] {
|
||||
if (current.spec.ancestors.length) {
|
||||
return current.spec.ancestors;
|
||||
}
|
||||
|
||||
let ancestors: string[];
|
||||
const parentName = current.spec.parent;
|
||||
if (!parentName) {
|
||||
ancestors = [];
|
||||
} else {
|
||||
const parent = groupsByName.get(parentName);
|
||||
if (parent) {
|
||||
ancestors = [parentName, ...visitAncestors(parent)];
|
||||
} else {
|
||||
ancestors = [parentName];
|
||||
}
|
||||
}
|
||||
|
||||
current.spec.ancestors = ancestors;
|
||||
return ancestors;
|
||||
}
|
||||
|
||||
for (const group of groups) {
|
||||
visitAncestors(group);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,15 +55,24 @@ 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([...user.spec.memberOf]);
|
||||
const transitiveMemberOf = new Set<string>();
|
||||
|
||||
user.spec.memberOf.forEach(groupName => {
|
||||
const group = groupsByName.get(groupName);
|
||||
|
||||
if (group) {
|
||||
group.spec.ancestors.forEach(g => transitiveMemberOf.add(g));
|
||||
const todo = [...user.spec.memberOf];
|
||||
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];
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user