|
@@ -1,21 +1,35 @@
|
|
|
-import type { IUserHasId } from '@growi/core';
|
|
|
|
|
|
|
+import type { IPage, IUserHasId } from '@growi/core';
|
|
|
import mongoose, { Types } from 'mongoose';
|
|
import mongoose, { Types } from 'mongoose';
|
|
|
-
|
|
|
|
|
import {
|
|
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';
|
|
|
|
|
|
|
+ afterEach,
|
|
|
|
|
+ beforeAll,
|
|
|
|
|
+ beforeEach,
|
|
|
|
|
+ describe,
|
|
|
|
|
+ expect,
|
|
|
|
|
+ it,
|
|
|
|
|
+ vi,
|
|
|
|
|
+} from 'vitest';
|
|
|
|
|
+import { mock } from 'vitest-mock-extended';
|
|
|
|
|
+
|
|
|
|
|
+import { getInstance } from '^/test/setup/crowi';
|
|
|
|
|
+
|
|
|
|
|
+import type Crowi from '~/server/crowi';
|
|
|
|
|
+import ExternalAccount from '~/server/models/external-account';
|
|
|
|
|
+import type { PageModel } from '~/server/models/page';
|
|
|
|
|
+import { configManager } from '~/server/service/config-manager';
|
|
|
|
|
+import instanciateExternalAccountService from '~/server/service/external-account';
|
|
|
|
|
+import type PassportService from '~/server/service/passport';
|
|
|
|
|
+import type { S2sMessagingService } from '~/server/service/s2s-messaging/base';
|
|
|
|
|
+
|
|
|
|
|
+import type {
|
|
|
|
|
+ ExternalUserGroupTreeNode,
|
|
|
|
|
+ IExternalUserGroup,
|
|
|
|
|
+ IExternalUserGroupHasId,
|
|
|
|
|
+} from '../../interfaces/external-user-group';
|
|
|
|
|
+import { ExternalGroupProviderType } from '../../interfaces/external-user-group';
|
|
|
|
|
+import ExternalUserGroup from '../models/external-user-group';
|
|
|
|
|
+import ExternalUserGroupRelation from '../models/external-user-group-relation';
|
|
|
|
|
+import ExternalUserGroupSyncService from './external-user-group-sync';
|
|
|
|
|
|
|
|
// dummy class to implement generateExternalUserGroupTrees which returns test data
|
|
// dummy class to implement generateExternalUserGroupTrees which returns test data
|
|
|
class TestExternalUserGroupSyncService extends ExternalUserGroupSyncService {
|
|
class TestExternalUserGroupSyncService extends ExternalUserGroupSyncService {
|
|
@@ -41,7 +55,6 @@ class TestExternalUserGroupSyncService extends ExternalUserGroupSyncService {
|
|
|
};
|
|
};
|
|
|
const parentNode: ExternalUserGroupTreeNode = {
|
|
const parentNode: ExternalUserGroupTreeNode = {
|
|
|
id: 'cn=parentGroup,ou=groups,dc=example,dc=org',
|
|
id: 'cn=parentGroup,ou=groups,dc=example,dc=org',
|
|
|
- // name is undefined
|
|
|
|
|
userInfos: [
|
|
userInfos: [
|
|
|
{
|
|
{
|
|
|
id: 'parentGroupUser',
|
|
id: 'parentGroupUser',
|
|
@@ -55,7 +68,6 @@ class TestExternalUserGroupSyncService extends ExternalUserGroupSyncService {
|
|
|
};
|
|
};
|
|
|
const grandParentNode: ExternalUserGroupTreeNode = {
|
|
const grandParentNode: ExternalUserGroupTreeNode = {
|
|
|
id: 'cn=grandParentGroup,ou=groups,dc=example,dc=org',
|
|
id: 'cn=grandParentGroup,ou=groups,dc=example,dc=org',
|
|
|
- // email is undefined
|
|
|
|
|
userInfos: [
|
|
userInfos: [
|
|
|
{
|
|
{
|
|
|
id: 'grandParentGroupUser',
|
|
id: 'grandParentGroupUser',
|
|
@@ -87,8 +99,6 @@ class TestExternalUserGroupSyncService extends ExternalUserGroupSyncService {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const testService = new TestExternalUserGroupSyncService(null, null);
|
|
|
|
|
-
|
|
|
|
|
const checkGroup = (
|
|
const checkGroup = (
|
|
|
group: IExternalUserGroupHasId,
|
|
group: IExternalUserGroupHasId,
|
|
|
expected: Omit<IExternalUserGroup, 'createdAt'>,
|
|
expected: Omit<IExternalUserGroup, 'createdAt'>,
|
|
@@ -107,7 +117,8 @@ const checkSync = async (autoGenerateUserOnGroupSync = true) => {
|
|
|
const grandParentGroup = await ExternalUserGroup.findOne({
|
|
const grandParentGroup = await ExternalUserGroup.findOne({
|
|
|
name: 'grandParentGroup',
|
|
name: 'grandParentGroup',
|
|
|
});
|
|
});
|
|
|
- checkGroup(grandParentGroup, {
|
|
|
|
|
|
|
+ expect(grandParentGroup).not.toBeNull();
|
|
|
|
|
+ checkGroup(grandParentGroup!, {
|
|
|
externalId: 'cn=grandParentGroup,ou=groups,dc=example,dc=org',
|
|
externalId: 'cn=grandParentGroup,ou=groups,dc=example,dc=org',
|
|
|
name: 'grandParentGroup',
|
|
name: 'grandParentGroup',
|
|
|
description: 'this is a grand parent group',
|
|
description: 'this is a grand parent group',
|
|
@@ -116,27 +127,30 @@ const checkSync = async (autoGenerateUserOnGroupSync = true) => {
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
const parentGroup = await ExternalUserGroup.findOne({ name: 'parentGroup' });
|
|
const parentGroup = await ExternalUserGroup.findOne({ name: 'parentGroup' });
|
|
|
- checkGroup(parentGroup, {
|
|
|
|
|
|
|
+ expect(parentGroup).not.toBeNull();
|
|
|
|
|
+ checkGroup(parentGroup!, {
|
|
|
externalId: 'cn=parentGroup,ou=groups,dc=example,dc=org',
|
|
externalId: 'cn=parentGroup,ou=groups,dc=example,dc=org',
|
|
|
name: 'parentGroup',
|
|
name: 'parentGroup',
|
|
|
description: 'this is a parent group',
|
|
description: 'this is a parent group',
|
|
|
provider: 'ldap',
|
|
provider: 'ldap',
|
|
|
- parent: grandParentGroup._id,
|
|
|
|
|
|
|
+ parent: grandParentGroup!._id,
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
const childGroup = await ExternalUserGroup.findOne({ name: 'childGroup' });
|
|
const childGroup = await ExternalUserGroup.findOne({ name: 'childGroup' });
|
|
|
- checkGroup(childGroup, {
|
|
|
|
|
|
|
+ expect(childGroup).not.toBeNull();
|
|
|
|
|
+ checkGroup(childGroup!, {
|
|
|
externalId: 'cn=childGroup,ou=groups,dc=example,dc=org',
|
|
externalId: 'cn=childGroup,ou=groups,dc=example,dc=org',
|
|
|
name: 'childGroup',
|
|
name: 'childGroup',
|
|
|
description: 'this is a child group',
|
|
description: 'this is a child group',
|
|
|
provider: 'ldap',
|
|
provider: 'ldap',
|
|
|
- parent: parentGroup._id,
|
|
|
|
|
|
|
+ parent: parentGroup!._id,
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
const previouslySyncedGroup = await ExternalUserGroup.findOne({
|
|
const previouslySyncedGroup = await ExternalUserGroup.findOne({
|
|
|
name: 'previouslySyncedGroup',
|
|
name: 'previouslySyncedGroup',
|
|
|
});
|
|
});
|
|
|
- checkGroup(previouslySyncedGroup, {
|
|
|
|
|
|
|
+ expect(previouslySyncedGroup).not.toBeNull();
|
|
|
|
|
+ checkGroup(previouslySyncedGroup!, {
|
|
|
externalId: 'cn=previouslySyncedGroup,ou=groups,dc=example,dc=org',
|
|
externalId: 'cn=previouslySyncedGroup,ou=groups,dc=example,dc=org',
|
|
|
name: 'previouslySyncedGroup',
|
|
name: 'previouslySyncedGroup',
|
|
|
description: 'this is a previouslySynced group',
|
|
description: 'this is a previouslySynced group',
|
|
@@ -145,16 +159,16 @@ const checkSync = async (autoGenerateUserOnGroupSync = true) => {
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
const grandParentGroupRelations = await ExternalUserGroupRelation.find({
|
|
const grandParentGroupRelations = await ExternalUserGroupRelation.find({
|
|
|
- relatedGroup: grandParentGroup._id,
|
|
|
|
|
|
|
+ relatedGroup: grandParentGroup!._id,
|
|
|
});
|
|
});
|
|
|
const parentGroupRelations = await ExternalUserGroupRelation.find({
|
|
const parentGroupRelations = await ExternalUserGroupRelation.find({
|
|
|
- relatedGroup: parentGroup._id,
|
|
|
|
|
|
|
+ relatedGroup: parentGroup!._id,
|
|
|
});
|
|
});
|
|
|
const childGroupRelations = await ExternalUserGroupRelation.find({
|
|
const childGroupRelations = await ExternalUserGroupRelation.find({
|
|
|
- relatedGroup: childGroup._id,
|
|
|
|
|
|
|
+ relatedGroup: childGroup!._id,
|
|
|
});
|
|
});
|
|
|
const previouslySyncedGroupRelations = await ExternalUserGroupRelation.find({
|
|
const previouslySyncedGroupRelations = await ExternalUserGroupRelation.find({
|
|
|
- relatedGroup: previouslySyncedGroup._id,
|
|
|
|
|
|
|
+ relatedGroup: previouslySyncedGroup!._id,
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
if (autoGenerateUserOnGroupSync) {
|
|
if (autoGenerateUserOnGroupSync) {
|
|
@@ -205,7 +219,7 @@ const checkSync = async (autoGenerateUserOnGroupSync = true) => {
|
|
|
'previouslySyncedGroupUser',
|
|
'previouslySyncedGroupUser',
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
- const userPages = await mongoose.model('Page').find({
|
|
|
|
|
|
|
+ const userPages = await mongoose.model<IPage>('Page').find({
|
|
|
path: {
|
|
path: {
|
|
|
$in: [
|
|
$in: [
|
|
|
'/user/childGroupUser',
|
|
'/user/childGroupUser',
|
|
@@ -225,16 +239,76 @@ const checkSync = async (autoGenerateUserOnGroupSync = true) => {
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
describe('ExternalUserGroupSyncService.syncExternalUserGroups', () => {
|
|
describe('ExternalUserGroupSyncService.syncExternalUserGroups', () => {
|
|
|
- let crowi: Crowi;
|
|
|
|
|
|
|
+ let testService: TestExternalUserGroupSyncService;
|
|
|
|
|
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
|
|
+ let Page: PageModel;
|
|
|
|
|
+ let rootPageId: Types.ObjectId;
|
|
|
|
|
+ let userPageId: Types.ObjectId;
|
|
|
|
|
|
|
|
beforeAll(async () => {
|
|
beforeAll(async () => {
|
|
|
- crowi = await getInstance();
|
|
|
|
|
|
|
+ // Initialize configManager
|
|
|
|
|
+ const s2sMessagingServiceMock = mock<S2sMessagingService>();
|
|
|
|
|
+ configManager.setS2sMessagingService(s2sMessagingServiceMock);
|
|
|
|
|
+ await configManager.loadConfigs();
|
|
|
|
|
+
|
|
|
|
|
+ const crowi: Crowi = await getInstance();
|
|
|
|
|
+
|
|
|
|
|
+ // Initialize models with crowi mock
|
|
|
|
|
+ const pageModule = await import('~/server/models/page');
|
|
|
|
|
+ Page = pageModule.default(crowi);
|
|
|
|
|
+
|
|
|
|
|
+ const userModule = await import('~/server/models/user/index');
|
|
|
|
|
+ userModule.default(crowi);
|
|
|
|
|
+
|
|
|
|
|
+ // Initialize services with mocked PassportService
|
|
|
await configManager.updateConfig('app:isV5Compatible', true);
|
|
await configManager.updateConfig('app:isV5Compatible', true);
|
|
|
- const passportService = new PassportService(crowi);
|
|
|
|
|
- instanciateExternalAccountService(passportService);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // Create PassportService mock with required methods for externalAccountService
|
|
|
|
|
+ const passportServiceMock = mock<PassportService>({
|
|
|
|
|
+ isSameUsernameTreatedAsIdenticalUser: vi.fn().mockReturnValue(false),
|
|
|
|
|
+ isSameEmailTreatedAsIdenticalUser: vi.fn().mockReturnValue(false),
|
|
|
|
|
+ });
|
|
|
|
|
+ instanciateExternalAccountService(passportServiceMock);
|
|
|
|
|
+
|
|
|
|
|
+ // Create root page and /user page for UserEvent.onActivated to work
|
|
|
|
|
+ rootPageId = new Types.ObjectId();
|
|
|
|
|
+ userPageId = new Types.ObjectId();
|
|
|
|
|
+
|
|
|
|
|
+ // Check if root page already exists
|
|
|
|
|
+ const existingRootPage = await Page.findOne({ path: '/' });
|
|
|
|
|
+ if (existingRootPage == null) {
|
|
|
|
|
+ await Page.insertMany([
|
|
|
|
|
+ {
|
|
|
|
|
+ _id: rootPageId,
|
|
|
|
|
+ path: '/',
|
|
|
|
|
+ grant: Page.GRANT_PUBLIC,
|
|
|
|
|
+ },
|
|
|
|
|
+ ]);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ rootPageId = existingRootPage._id;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Check if /user page already exists
|
|
|
|
|
+ const existingUserPage = await Page.findOne({ path: '/user' });
|
|
|
|
|
+ if (existingUserPage == null) {
|
|
|
|
|
+ await Page.insertMany([
|
|
|
|
|
+ {
|
|
|
|
|
+ _id: userPageId,
|
|
|
|
|
+ path: '/user',
|
|
|
|
|
+ grant: Page.GRANT_PUBLIC,
|
|
|
|
|
+ parent: rootPageId,
|
|
|
|
|
+ isEmpty: true,
|
|
|
|
|
+ },
|
|
|
|
|
+ ]);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ userPageId = existingUserPage._id;
|
|
|
|
|
+ }
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
beforeEach(async () => {
|
|
beforeEach(async () => {
|
|
|
|
|
+ // Create new testService instance for each test to reset syncStatus
|
|
|
|
|
+ testService = new TestExternalUserGroupSyncService(null, null);
|
|
|
|
|
+
|
|
|
await ExternalUserGroup.create({
|
|
await ExternalUserGroup.create({
|
|
|
name: 'nameBeforeEdit',
|
|
name: 'nameBeforeEdit',
|
|
|
description: 'this is a description before edit',
|
|
description: 'this is a description before edit',
|
|
@@ -284,7 +358,7 @@ describe('ExternalUserGroupSyncService.syncExternalUserGroups', () => {
|
|
|
'external-user-group:ldap:preserveDeletedGroups': false,
|
|
'external-user-group:ldap:preserveDeletedGroups': false,
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- beforeAll(async () => {
|
|
|
|
|
|
|
+ beforeEach(async () => {
|
|
|
await configManager.updateConfigs(configParams);
|
|
await configManager.updateConfigs(configParams);
|
|
|
});
|
|
});
|
|
|
|
|
|
|
@@ -300,7 +374,7 @@ describe('ExternalUserGroupSyncService.syncExternalUserGroups', () => {
|
|
|
'external-user-group:ldap:preserveDeletedGroups': true,
|
|
'external-user-group:ldap:preserveDeletedGroups': true,
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- beforeAll(async () => {
|
|
|
|
|
|
|
+ beforeEach(async () => {
|
|
|
await configManager.updateConfigs(configParams);
|
|
await configManager.updateConfigs(configParams);
|
|
|
});
|
|
});
|
|
|
|
|
|
|
@@ -316,7 +390,7 @@ describe('ExternalUserGroupSyncService.syncExternalUserGroups', () => {
|
|
|
'external-user-group:ldap:preserveDeletedGroups': false,
|
|
'external-user-group:ldap:preserveDeletedGroups': false,
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- beforeAll(async () => {
|
|
|
|
|
|
|
+ beforeEach(async () => {
|
|
|
await configManager.updateConfigs(configParams);
|
|
await configManager.updateConfigs(configParams);
|
|
|
|
|
|
|
|
const groupId = new Types.ObjectId();
|
|
const groupId = new Types.ObjectId();
|