Regression: LDAP Issues (#23306)

pull/22941/head
pierre-lehnen-rc 4 years ago committed by GitHub
parent 579e7d23d5
commit 13e0983ce2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 29
      app/api/server/v1/ldap.ts
  2. 5
      client/contexts/ServerContext/endpoints/v1/ldap.ts
  3. 101
      client/views/admin/settings/groups/LDAPGroupPage.tsx
  4. 3
      packages/rocketchat-i18n/i18n/en.i18n.json
  5. 5
      server/lib/ldap/Connection.ts
  6. 18
      server/lib/ldap/Manager.ts
  7. 1
      server/sdk/types/ILDAPService.ts
  8. 4
      server/services/ldap/service.ts

@ -1,10 +1,11 @@
import { Match, check } from 'meteor/check';
import { hasRole } from '../../../authorization/server';
import { settings } from '../../../settings/server';
import { API } from '../api';
import { SystemLogger } from '../../../../server/lib/logger/system';
import { LDAP } from '../../../../server/sdk';
API.v1.addRoute('ldap.testConnection', { authRequired: true }, {
post() {
if (!this.userId) {
@ -31,3 +32,29 @@ API.v1.addRoute('ldap.testConnection', { authRequired: true }, {
});
},
});
API.v1.addRoute('ldap.testSearch', { authRequired: true }, {
post() {
check(this.bodyParams, Match.ObjectIncluding({
username: String,
}));
if (!this.userId) {
throw new Error('error-invalid-user');
}
if (!hasRole(this.userId, 'admin')) {
throw new Error('error-not-authorized');
}
if (settings.get('LDAP_Enable') !== true) {
throw new Error('LDAP_disabled');
}
Promise.await(LDAP.testSearch(this.bodyParams.username));
return API.v1.success({
message: 'LDAP_User_Found',
});
},
});

@ -6,6 +6,11 @@ export type LDAPEndpoints = {
message: TranslationKey;
};
};
'ldap.testSearch': {
POST: (params: { username: string }) => {
message: TranslationKey;
};
};
'ldap.syncNow': {
POST: () => {
message: TranslationKey;

@ -1,9 +1,11 @@
import { Button } from '@rocket.chat/fuselage';
import React, { memo, useMemo } from 'react';
import { Button, Box, TextInput, Field } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import React, { FormEvent, memo, useMemo } from 'react';
import type { ISetting } from '../../../../../definition/ISetting';
import GenericModal from '../../../../components/GenericModal';
import { useEditableSettings } from '../../../../contexts/EditableSettingsContext';
import { useModal } from '../../../../contexts/ModalContext';
import { useSetModal } from '../../../../contexts/ModalContext';
import { useEndpoint } from '../../../../contexts/ServerContext';
import { useSetting } from '../../../../contexts/SettingsContext';
import { useToastMessageDispatch } from '../../../../contexts/ToastMessagesContext';
@ -15,9 +17,11 @@ function LDAPGroupPage({ _id, ...group }: ISetting): JSX.Element {
const dispatchToastMessage = useToastMessageDispatch();
const testConnection = useEndpoint('POST', 'ldap.testConnection');
const syncNow = useEndpoint('POST', 'ldap.syncNow');
const testSearch = useEndpoint('POST', 'ldap.testSearch');
const ldapEnabled = useSetting('LDAP_Enable');
const ldapSyncEnabled = useSetting('LDAP_Background_Sync') && ldapEnabled;
const modal = useModal();
const setModal = useSetModal();
const closeModal = useMutableCallback(() => setModal());
const editableSettings = useEditableSettings(
useMemo(
@ -45,28 +49,68 @@ function LDAPGroupPage({ _id, ...group }: ISetting): JSX.Element {
const handleSyncNowButtonClick = async (): Promise<void> => {
try {
await testConnection(undefined);
// #ToDo: Switch to modal.setModal
modal.open(
{
title: t('Execute_Synchronization_Now'),
text: t('LDAP_Sync_Now_Description'),
confirmButtonText: t('Sync'),
showCancelButton: true,
closeOnConfirm: true,
closeOnCancel: true,
},
async (isConfirm: boolean): Promise<void> => {
if (!isConfirm) {
return;
}
const confirmSync = async (): Promise<void> => {
try {
const { message } = await syncNow(undefined);
dispatchToastMessage({ type: 'success', message: t(message) });
} catch (error) {
dispatchToastMessage({ type: 'error', message: error });
}
};
try {
const { message } = await syncNow(undefined);
dispatchToastMessage({ type: 'success', message: t(message) });
} catch (error) {
dispatchToastMessage({ type: 'error', message: error });
}
},
setModal(
<GenericModal
variant='info'
confirmText={t('Sync')}
cancelText={t('Cancel')}
title={t('Execute_Synchronization_Now')}
onConfirm={confirmSync}
onClose={closeModal}
>
{t('LDAP_Sync_Now_Description')}
</GenericModal>,
);
} catch (error) {
dispatchToastMessage({ type: 'error', message: error });
}
};
const handleSearchTestButtonClick = async (): Promise<void> => {
try {
await testConnection(undefined);
let username = '';
const handleChangeUsername = (event: FormEvent<HTMLInputElement>): void => {
username = event.currentTarget.value;
};
const confirmSearch = async (): Promise<void> => {
try {
const { message } = await testSearch({ username });
dispatchToastMessage({ type: 'success', message: t(message) });
} catch (error) {
dispatchToastMessage({ type: 'error', message: error });
}
};
setModal(
<GenericModal
variant='info'
confirmText={t('Search')}
cancelText={t('Cancel')}
title={t('Test_LDAP_Search')}
onConfirm={confirmSearch}
onClose={closeModal}
>
<Field>
<Box display='flex'>
<Field.Label>{t('LDAP_Username_To_Search')}</Field.Label>
</Box>
<Field.Row>
<TextInput onChange={handleChangeUsername} />
</Field.Row>
</Field>
</GenericModal>,
);
} catch (error) {
dispatchToastMessage({ type: 'error', message: error });
@ -84,6 +128,11 @@ function LDAPGroupPage({ _id, ...group }: ISetting): JSX.Element {
disabled={!ldapEnabled || changed}
onClick={handleTestConnectionButtonClick}
/>
<Button
children={t('Test_LDAP_Search')}
disabled={!ldapEnabled || changed}
onClick={handleSearchTestButtonClick}
/>
{ldapSyncEnabled && (
<Button
children={t('LDAP_Sync_Now')}
@ -91,7 +140,7 @@ function LDAPGroupPage({ _id, ...group }: ISetting): JSX.Element {
onClick={handleSyncNowButtonClick}
/>
)}
<Button is='a' href='https://go.rocket.chat/i/ldap-doc' target='_blank'>
<Button is='a' href='https://go.rocket.chat/i/ldap-docs' target='_blank'>
{t('LDAP_Documentation')}
</Button>
</>

@ -2555,6 +2555,7 @@
"LDAP_Timeout_Description": "How many mileseconds wait for a search result before return an error",
"LDAP_Unique_Identifier_Field": "Unique Identifier Field",
"LDAP_Unique_Identifier_Field_Description": "Which field will be used to link the LDAP user and the Rocket.Chat user. You can inform multiple values separated by comma to try to get the value from LDAP record.<br/>Default value is `objectGUID,ibm-entryUUID,GUID,dominoUNID,nsuniqueId,uidNumber`",
"LDAP_User_Found": "LDAP User Found",
"LDAP_User_Search_AttributesToQuery": "Attributes to Query",
"LDAP_User_Search_AttributesToQuery_Description": "Specify which attributes should be returned on LDAP queries, separating them with commas. Defaults to everything. `*` represents all regular attributes and `+` represents all operational attributes. Make sure to include every attribute that is used by every Rocket.Chat sync option.",
"LDAP_User_Search_Field": "Search Field",
@ -2564,6 +2565,7 @@
"LDAP_User_Search_Scope": "Scope",
"LDAP_Username_Field": "Username Field",
"LDAP_Username_Field_Description": "Which field will be used as *username* for new users. Leave empty to use the username informed on login page.<br/>You can use template tags too, like `#{givenName}.#{sn}`.<br/>Default value is `sAMAccountName`.",
"LDAP_Username_To_Search": "Username to search",
"LDAP_Validate_Teams_For_Each_Login": "Validate mapping for each login",
"LDAP_Validate_Teams_For_Each_Login_Description": "Determine if users' teams should be updated every time they login to Rocket.Chat. If this is turned off the team will be loaded only on their first login.",
"Lead_capture_email_regex": "Lead capture email regex",
@ -4075,6 +4077,7 @@
"Terms": "Terms",
"Test_Connection": "Test Connection",
"Test_Desktop_Notifications": "Test Desktop Notifications",
"Test_LDAP_Search": "Test LDAP Search",
"Texts": "Texts",
"Thank_you_exclamation_mark": "Thank you!",
"Thank_you_for_your_feedback": "Thank you for your feedback",

@ -4,7 +4,6 @@ import { settings } from '../../../app/settings/server';
import type { ILDAPConnectionOptions, LDAPEncryptionType, LDAPSearchScope } from '../../../definition/ldap/ILDAPOptions';
import type { ILDAPEntry } from '../../../definition/ldap/ILDAPEntry';
import type { ILDAPCallback, ILDAPPageCallback } from '../../../definition/ldap/ILDAPCallback';
import { callbacks } from '../../../app/callbacks/server';
import { logger, connLogger, searchLogger, authLogger, bindLogger, mapLogger } from './Logger';
import { getLDAPConditionalSetting } from './getLDAPConditionalSetting';
@ -550,8 +549,8 @@ export class LDAPConnection {
this.usingAuthentication = true;
}
protected async runBeforeSearch(searchOptions: ldapjs.SearchOptions): Promise<void> {
callbacks.run('beforeLDAPSearch', searchOptions, this);
protected async runBeforeSearch(_searchOptions: ldapjs.SearchOptions): Promise<void> {
this.maybeBindDN();
}
/*

@ -67,6 +67,24 @@ export class LDAPManager {
}
}
public static async testSearch(username: string): Promise<void> {
const escapedUsername = ldapEscape.filter`${ username }`;
const ldap = new LDAPConnection();
try {
await ldap.connect();
const users = await ldap.searchByUsername(escapedUsername);
if (users.length !== 1) {
logger.debug(`Search returned ${ users.length } records for ${ escapedUsername }`);
throw new Error('User not found');
}
} catch (error) {
logger.error(error);
throw error;
}
}
public static syncUserAvatar(user: IUser, ldapUser: ILDAPEntry): void {
if (!user?._id || settings.get('LDAP_Sync_User_Avatar') !== true) {
return;

@ -3,4 +3,5 @@ import type { LDAPLoginResult } from '../../../definition/ldap/ILDAPLoginResult'
export interface ILDAPService {
loginRequest(username: string, password: string): Promise<LDAPLoginResult>;
testConnection(): Promise<void>;
testSearch(username: string): Promise<void>;
}

@ -13,4 +13,8 @@ export class LDAPService extends ServiceClass implements ILDAPService {
async testConnection(): Promise<void> {
return LDAPManager.testConnection();
}
async testSearch(username: string): Promise<void> {
return LDAPManager.testSearch(username);
}
}

Loading…
Cancel
Save