| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347 |
- import type { IUserHasId } from '@growi/core';
- import mongoose, { Types } from 'mongoose';
- import {
- ExternalGroupProviderType,
- type ExternalUserGroupTreeNode,
- type IExternalUserGroup,
- type IExternalUserGroupHasId,
- } from '../../../src/features/external-user-group/interfaces/external-user-group';
- import ExternalUserGroup from '../../../src/features/external-user-group/server/models/external-user-group';
- import ExternalUserGroupRelation from '../../../src/features/external-user-group/server/models/external-user-group-relation';
- import ExternalUserGroupSyncService from '../../../src/features/external-user-group/server/service/external-user-group-sync';
- import type Crowi from '../../../src/server/crowi';
- import ExternalAccount from '../../../src/server/models/external-account';
- import { configManager } from '../../../src/server/service/config-manager';
- import instanciateExternalAccountService from '../../../src/server/service/external-account';
- import PassportService from '../../../src/server/service/passport';
- import { getInstance } from '../setup-crowi';
- // dummy class to implement generateExternalUserGroupTrees which returns test data
- class TestExternalUserGroupSyncService extends ExternalUserGroupSyncService {
- constructor(s2sMessagingService, socketIoService) {
- super('ldap', s2sMessagingService, socketIoService);
- this.authProviderType = ExternalGroupProviderType.ldap;
- }
- async generateExternalUserGroupTrees(): Promise<ExternalUserGroupTreeNode[]> {
- const childNode: ExternalUserGroupTreeNode = {
- id: 'cn=childGroup,ou=groups,dc=example,dc=org',
- userInfos: [
- {
- id: 'childGroupUser',
- username: 'childGroupUser',
- name: 'Child Group User',
- email: 'user@childgroup.com',
- },
- ],
- childGroupNodes: [],
- name: 'childGroup',
- description: 'this is a child group',
- };
- const parentNode: ExternalUserGroupTreeNode = {
- id: 'cn=parentGroup,ou=groups,dc=example,dc=org',
- // name is undefined
- userInfos: [
- {
- id: 'parentGroupUser',
- username: 'parentGroupUser',
- email: 'user@parentgroup.com',
- },
- ],
- childGroupNodes: [childNode],
- name: 'parentGroup',
- description: 'this is a parent group',
- };
- const grandParentNode: ExternalUserGroupTreeNode = {
- id: 'cn=grandParentGroup,ou=groups,dc=example,dc=org',
- // email is undefined
- userInfos: [
- {
- id: 'grandParentGroupUser',
- username: 'grandParentGroupUser',
- name: 'Grand Parent Group User',
- },
- ],
- childGroupNodes: [parentNode],
- name: 'grandParentGroup',
- description: 'this is a grand parent group',
- };
- const previouslySyncedNode: ExternalUserGroupTreeNode = {
- id: 'cn=previouslySyncedGroup,ou=groups,dc=example,dc=org',
- userInfos: [
- {
- id: 'previouslySyncedGroupUser',
- username: 'previouslySyncedGroupUser',
- name: 'Root Group User',
- email: 'user@previouslySyncedgroup.com',
- },
- ],
- childGroupNodes: [],
- name: 'previouslySyncedGroup',
- description: 'this is a previouslySynced group',
- };
- return [grandParentNode, previouslySyncedNode];
- }
- }
- const testService = new TestExternalUserGroupSyncService(null, null);
- const checkGroup = (
- group: IExternalUserGroupHasId,
- expected: Omit<IExternalUserGroup, 'createdAt'>,
- ) => {
- const actual = {
- name: group.name,
- parent: group.parent,
- description: group.description,
- externalId: group.externalId,
- provider: group.provider,
- };
- expect(actual).toStrictEqual(expected);
- };
- const checkSync = async (autoGenerateUserOnGroupSync = true) => {
- const grandParentGroup = await ExternalUserGroup.findOne({
- name: 'grandParentGroup',
- });
- checkGroup(grandParentGroup, {
- externalId: 'cn=grandParentGroup,ou=groups,dc=example,dc=org',
- name: 'grandParentGroup',
- description: 'this is a grand parent group',
- provider: 'ldap',
- parent: null,
- });
- const parentGroup = await ExternalUserGroup.findOne({ name: 'parentGroup' });
- checkGroup(parentGroup, {
- externalId: 'cn=parentGroup,ou=groups,dc=example,dc=org',
- name: 'parentGroup',
- description: 'this is a parent group',
- provider: 'ldap',
- parent: grandParentGroup._id,
- });
- const childGroup = await ExternalUserGroup.findOne({ name: 'childGroup' });
- checkGroup(childGroup, {
- externalId: 'cn=childGroup,ou=groups,dc=example,dc=org',
- name: 'childGroup',
- description: 'this is a child group',
- provider: 'ldap',
- parent: parentGroup._id,
- });
- const previouslySyncedGroup = await ExternalUserGroup.findOne({
- name: 'previouslySyncedGroup',
- });
- checkGroup(previouslySyncedGroup, {
- externalId: 'cn=previouslySyncedGroup,ou=groups,dc=example,dc=org',
- name: 'previouslySyncedGroup',
- description: 'this is a previouslySynced group',
- provider: 'ldap',
- parent: null,
- });
- const grandParentGroupRelations = await ExternalUserGroupRelation.find({
- relatedGroup: grandParentGroup._id,
- });
- const parentGroupRelations = await ExternalUserGroupRelation.find({
- relatedGroup: parentGroup._id,
- });
- const childGroupRelations = await ExternalUserGroupRelation.find({
- relatedGroup: childGroup._id,
- });
- const previouslySyncedGroupRelations = await ExternalUserGroupRelation.find({
- relatedGroup: previouslySyncedGroup._id,
- });
- if (autoGenerateUserOnGroupSync) {
- expect(grandParentGroupRelations.length).toBe(3);
- const populatedGrandParentGroupRelations = await Promise.all(
- grandParentGroupRelations.map((relation) => {
- return relation.populate<{ relatedUser: IUserHasId }>('relatedUser');
- }),
- );
- expect(populatedGrandParentGroupRelations[0].relatedUser.username).toBe(
- 'grandParentGroupUser',
- );
- expect(populatedGrandParentGroupRelations[1].relatedUser.username).toBe(
- 'parentGroupUser',
- );
- expect(populatedGrandParentGroupRelations[2].relatedUser.username).toBe(
- 'childGroupUser',
- );
- expect(parentGroupRelations.length).toBe(2);
- const populatedParentGroupRelations = await Promise.all(
- parentGroupRelations.map((relation) => {
- return relation.populate<{ relatedUser: IUserHasId }>('relatedUser');
- }),
- );
- expect(populatedParentGroupRelations[0].relatedUser.username).toBe(
- 'parentGroupUser',
- );
- expect(populatedParentGroupRelations[1].relatedUser.username).toBe(
- 'childGroupUser',
- );
- expect(childGroupRelations.length).toBe(1);
- const childGroupUser = (
- await childGroupRelations[0].populate<{ relatedUser: IUserHasId }>(
- 'relatedUser',
- )
- )?.relatedUser;
- expect(childGroupUser?.username).toBe('childGroupUser');
- expect(previouslySyncedGroupRelations.length).toBe(1);
- const previouslySyncedGroupUser = (
- await previouslySyncedGroupRelations[0].populate<{
- relatedUser: IUserHasId;
- }>('relatedUser')
- )?.relatedUser;
- expect(previouslySyncedGroupUser?.username).toBe(
- 'previouslySyncedGroupUser',
- );
- const userPages = await mongoose.model('Page').find({
- path: {
- $in: [
- '/user/childGroupUser',
- '/user/parentGroupUser',
- '/user/grandParentGroupUser',
- '/user/previouslySyncedGroupUser',
- ],
- },
- });
- expect(userPages.length).toBe(4);
- } else {
- expect(grandParentGroupRelations.length).toBe(0);
- expect(parentGroupRelations.length).toBe(0);
- expect(childGroupRelations.length).toBe(0);
- expect(previouslySyncedGroupRelations.length).toBe(0);
- }
- };
- describe('ExternalUserGroupSyncService.syncExternalUserGroups', () => {
- let crowi: Crowi;
- beforeAll(async () => {
- crowi = await getInstance();
- await configManager.updateConfig('app:isV5Compatible', true);
- const passportService = new PassportService(crowi);
- instanciateExternalAccountService(passportService);
- });
- beforeEach(async () => {
- await ExternalUserGroup.create({
- name: 'nameBeforeEdit',
- description: 'this is a description before edit',
- externalId: 'cn=previouslySyncedGroup,ou=groups,dc=example,dc=org',
- provider: 'ldap',
- });
- });
- afterEach(async () => {
- await ExternalUserGroup.deleteMany();
- await ExternalUserGroupRelation.deleteMany();
- await mongoose.model('User').deleteMany({
- username: {
- $in: [
- 'childGroupUser',
- 'parentGroupUser',
- 'grandParentGroupUser',
- 'previouslySyncedGroupUser',
- ],
- },
- });
- await ExternalAccount.deleteMany({
- accountId: {
- $in: [
- 'childGroupUser',
- 'parentGroupUser',
- 'grandParentGroupUser',
- 'previouslySyncedGroupUser',
- ],
- },
- });
- await mongoose.model('Page').deleteMany({
- path: {
- $in: [
- '/user/childGroupUser',
- '/user/parentGroupUser',
- '/user/grandParentGroupUser',
- '/user/previouslySyncedGroupUser',
- ],
- },
- });
- });
- describe('When autoGenerateUserOnGroupSync is true', () => {
- const configParams = {
- 'external-user-group:ldap:autoGenerateUserOnGroupSync': true,
- 'external-user-group:ldap:preserveDeletedGroups': false,
- };
- beforeAll(async () => {
- await configManager.updateConfigs(configParams);
- });
- it('syncs groups with new users', async () => {
- await testService.syncExternalUserGroups();
- await checkSync();
- });
- });
- describe('When autoGenerateUserOnGroupSync is false', () => {
- const configParams = {
- 'external-user-group:ldap:autoGenerateUserOnGroupSync': false,
- 'external-user-group:ldap:preserveDeletedGroups': true,
- };
- beforeAll(async () => {
- await configManager.updateConfigs(configParams);
- });
- it('syncs groups without new users', async () => {
- await testService.syncExternalUserGroups();
- await checkSync(false);
- });
- });
- describe('When preserveDeletedGroups is false', () => {
- const configParams = {
- 'external-user-group:ldap:autoGenerateUserOnGroupSync': true,
- 'external-user-group:ldap:preserveDeletedGroups': false,
- };
- beforeAll(async () => {
- await configManager.updateConfigs(configParams);
- const groupId = new Types.ObjectId();
- const userId = new Types.ObjectId();
- await ExternalUserGroup.create({
- _id: groupId,
- name: 'non existent group',
- externalId: 'cn=nonExistentGroup,ou=groups,dc=example,dc=org',
- provider: 'ldap',
- });
- await mongoose
- .model('User')
- .create({ _id: userId, username: 'nonExistentGroupUser' });
- await ExternalUserGroupRelation.create({
- relatedUser: userId,
- relatedGroup: groupId,
- });
- });
- it('syncs groups and deletes groups that do not exist externally', async () => {
- await testService.syncExternalUserGroups();
- await checkSync();
- expect(await ExternalUserGroup.countDocuments()).toBe(4);
- expect(await ExternalUserGroupRelation.countDocuments()).toBe(7);
- });
- });
- });
|