|
|
@@ -1,8 +1,6 @@
|
|
|
-import { ExternalGroupProviderType, ExternalUserGroupTreeNode } from '~/interfaces/external-user-group';
|
|
|
-import { IUserHasId } from '~/interfaces/user';
|
|
|
+import { ExternalGroupProviderType, ExternalUserGroupTreeNode, ExternalUserInfo } from '~/interfaces/external-user-group';
|
|
|
|
|
|
import { configManager } from '../config-manager';
|
|
|
-import ExternalAccountService from '../external-account';
|
|
|
import LdapService, { SearchResultEntry } from '../ldap';
|
|
|
|
|
|
import ExternalUserGroupSyncService from './external-user-group-sync-service';
|
|
|
@@ -11,50 +9,10 @@ class LdapUserGroupSyncService extends ExternalUserGroupSyncService {
|
|
|
|
|
|
ldapService: LdapService;
|
|
|
|
|
|
- externalAccountService: ExternalAccountService;
|
|
|
-
|
|
|
- crowi: any;
|
|
|
-
|
|
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
|
constructor(crowi: any, userBindUsername?: string, userBindPassword?: string) {
|
|
|
- super(ExternalGroupProviderType.ldap);
|
|
|
- this.crowi = crowi;
|
|
|
+ super(crowi, ExternalGroupProviderType.ldap, 'ldap');
|
|
|
this.ldapService = new LdapService(userBindUsername, userBindPassword);
|
|
|
- this.externalAccountService = new ExternalAccountService(crowi);
|
|
|
- }
|
|
|
-
|
|
|
- async getMemberUser(externalUserId: string): Promise<IUserHasId | null> {
|
|
|
- const groupMembershipAttributeType = configManager?.getConfig('crowi', 'external-user-group:ldap:groupMembershipAttributeType');
|
|
|
-
|
|
|
- const getUser = async() => {
|
|
|
- if (groupMembershipAttributeType === 'DN') {
|
|
|
- return this.ldapService.search(undefined, externalUserId, 'base');
|
|
|
- }
|
|
|
- if (groupMembershipAttributeType === 'UID') {
|
|
|
- return this.ldapService.search(`(uid=${externalUserId})`, undefined);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- let userEntryArr: SearchResultEntry[] | undefined;
|
|
|
- try {
|
|
|
- userEntryArr = await getUser();
|
|
|
- }
|
|
|
- catch (e) {
|
|
|
- throw Error('external_user_group.ldap.user_search_failed');
|
|
|
- }
|
|
|
-
|
|
|
- if (userEntryArr != null && userEntryArr.length > 0) {
|
|
|
- const userEntry = userEntryArr[0];
|
|
|
- const uid = this.ldapService.getStringValFromSearchResultEntry(userEntry, 'uid');
|
|
|
- if (uid != null) {
|
|
|
- const externalAccount = await this.getExternalAccount(uid, userEntry);
|
|
|
- if (externalAccount != null) {
|
|
|
- return externalAccount.getPopulatedUser();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return null;
|
|
|
}
|
|
|
|
|
|
async generateExternalUserGroupTrees(): Promise<ExternalUserGroupTreeNode[]> {
|
|
|
@@ -77,31 +35,34 @@ class LdapUserGroupSyncService extends ExternalUserGroupSyncService {
|
|
|
// so filter values of groupChildGroupAttribute to ones that include groupBase
|
|
|
return this.ldapService.getArrayValFromSearchResultEntry(groupEntry, groupChildGroupAttribute).filter(attr => attr.includes(groupBase));
|
|
|
};
|
|
|
- const getExternalUserIdsFromGroupEntry = (groupEntry: SearchResultEntry) => {
|
|
|
+ const getUserIdsFromGroupEntry = (groupEntry: SearchResultEntry) => {
|
|
|
// groupChildGroupAttribute and groupMembershipAttribute may be the same,
|
|
|
// so filter values of groupMembershipAttribute to ones that does not include groupBase
|
|
|
return this.ldapService.getArrayValFromSearchResultEntry(groupEntry, groupMembershipAttribute).filter(attr => !attr.includes(groupBase));
|
|
|
};
|
|
|
|
|
|
- const convert = (entry: SearchResultEntry, converted: string[]): ExternalUserGroupTreeNode | null => {
|
|
|
+ const convert = async(entry: SearchResultEntry, converted: string[]): Promise<ExternalUserGroupTreeNode | null> => {
|
|
|
if (converted.includes(entry.objectName)) {
|
|
|
throw Error('external_user_group.ldap.circular_reference');
|
|
|
}
|
|
|
converted.push(entry.objectName);
|
|
|
|
|
|
- const externalUserIds = getExternalUserIdsFromGroupEntry(entry);
|
|
|
+ const userIds = getUserIdsFromGroupEntry(entry);
|
|
|
+ const userInfos = (await Promise.all(userIds.map((id) => {
|
|
|
+ return this.getUserInfo(id);
|
|
|
+ }))).filter((info): info is NonNullable<ExternalUserInfo> => info != null);
|
|
|
const name = this.ldapService.getStringValFromSearchResultEntry(entry, groupNameAttribute);
|
|
|
const description = this.ldapService.getStringValFromSearchResultEntry(entry, groupDescriptionAttribute);
|
|
|
const childGroupDNs = getChildGroupDnsFromGroupEntry(entry);
|
|
|
|
|
|
- const childGroupNodes: ExternalUserGroupTreeNode[] = childGroupDNs.map((dn) => {
|
|
|
+ const childGroupNodes: ExternalUserGroupTreeNode[] = (await Promise.all(childGroupDNs.map((dn) => {
|
|
|
const childEntry = groupEntries.find(ge => ge.objectName === dn);
|
|
|
return childEntry != null ? convert(childEntry, converted) : null;
|
|
|
- }).filter((node): node is NonNullable<ExternalUserGroupTreeNode> => node != null);
|
|
|
+ }))).filter((node): node is NonNullable<ExternalUserGroupTreeNode> => node != null);
|
|
|
|
|
|
return name != null ? {
|
|
|
id: entry.objectName,
|
|
|
- externalUserIds,
|
|
|
+ userInfos,
|
|
|
childGroupNodes,
|
|
|
name,
|
|
|
description,
|
|
|
@@ -118,32 +79,50 @@ class LdapUserGroupSyncService extends ExternalUserGroupSyncService {
|
|
|
return !allChildGroupDNs.has(entry.objectName);
|
|
|
});
|
|
|
|
|
|
- return rootEntries.map(entry => convert(entry, [])).filter((node): node is NonNullable<ExternalUserGroupTreeNode> => node != null);
|
|
|
+ return (await Promise.all(rootEntries.map(entry => convert(entry, [])))).filter((node): node is NonNullable<ExternalUserGroupTreeNode> => node != null);
|
|
|
}
|
|
|
|
|
|
- private async getExternalAccount(uid: string, userEntry: SearchResultEntry) {
|
|
|
- const autoGenerateUserOnLDAPGroupSync = configManager?.getConfig('crowi', 'external-user-group:ldap:autoGenerateUserOnGroupSync');
|
|
|
-
|
|
|
- if (autoGenerateUserOnLDAPGroupSync) {
|
|
|
- const attrMapUsername = this.crowi.passportService.getLdapAttrNameMappedToUsername();
|
|
|
- const attrMapName = this.crowi.passportService.getLdapAttrNameMappedToName();
|
|
|
- const attrMapMail = this.crowi.passportService.getLdapAttrNameMappedToMail();
|
|
|
- const usernameToBeRegistered = attrMapUsername === 'uid' ? uid : this.ldapService.getStringValFromSearchResultEntry(userEntry, attrMapUsername);
|
|
|
- const nameToBeRegistered = this.ldapService.getStringValFromSearchResultEntry(userEntry, attrMapName);
|
|
|
- const mailToBeRegistered = this.ldapService.getStringValFromSearchResultEntry(userEntry, attrMapMail);
|
|
|
-
|
|
|
- const userInfo = {
|
|
|
- id: uid,
|
|
|
- username: usernameToBeRegistered,
|
|
|
- name: nameToBeRegistered,
|
|
|
- email: mailToBeRegistered,
|
|
|
- };
|
|
|
-
|
|
|
- return this.externalAccountService.getOrCreateUser(userInfo, 'ldap');
|
|
|
+ private async getUserInfo(userId: string): Promise<ExternalUserInfo | null> {
|
|
|
+ const groupMembershipAttributeType = configManager?.getConfig('crowi', 'external-user-group:ldap:groupMembershipAttributeType');
|
|
|
+ const attrMapUsername = this.crowi.passportService.getLdapAttrNameMappedToUsername();
|
|
|
+ const attrMapName = this.crowi.passportService.getLdapAttrNameMappedToName();
|
|
|
+ const attrMapMail = this.crowi.passportService.getLdapAttrNameMappedToMail();
|
|
|
+
|
|
|
+ // get full user info from LDAP server using externalUserInfo (DN or UID)
|
|
|
+ const getUserEntries = async() => {
|
|
|
+ if (groupMembershipAttributeType === 'DN') {
|
|
|
+ return this.ldapService.search(undefined, userId, 'base');
|
|
|
+ }
|
|
|
+ if (groupMembershipAttributeType === 'UID') {
|
|
|
+ return this.ldapService.search(`(uid=${userId})`, undefined);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ let userEntries: SearchResultEntry[] | undefined;
|
|
|
+ try {
|
|
|
+ userEntries = await getUserEntries();
|
|
|
+ }
|
|
|
+ catch (e) {
|
|
|
+ throw Error('external_user_group.ldap.user_search_failed');
|
|
|
}
|
|
|
|
|
|
- return this.crowi.models.ExternalAccount
|
|
|
- .findOne({ providerType: 'ldap', accountId: uid });
|
|
|
+ if (userEntries != null && userEntries.length > 0) {
|
|
|
+ const userEntry = userEntries[0];
|
|
|
+ const uid = this.ldapService.getStringValFromSearchResultEntry(userEntry, 'uid');
|
|
|
+ if (uid != null) {
|
|
|
+ const usernameToBeRegistered = attrMapUsername === 'uid' ? uid : this.ldapService.getStringValFromSearchResultEntry(userEntry, attrMapUsername);
|
|
|
+ const nameToBeRegistered = this.ldapService.getStringValFromSearchResultEntry(userEntry, attrMapName);
|
|
|
+ const mailToBeRegistered = this.ldapService.getStringValFromSearchResultEntry(userEntry, attrMapMail);
|
|
|
+
|
|
|
+ return usernameToBeRegistered != null ? {
|
|
|
+ id: uid,
|
|
|
+ username: usernameToBeRegistered || '',
|
|
|
+ name: nameToBeRegistered || '',
|
|
|
+ email: mailToBeRegistered,
|
|
|
+ } : null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
}
|
|
|
|
|
|
}
|