Skip to content

Commit

Permalink
feat: Add setting to synchronize LDAP info on OAuth logins (RocketCha…
Browse files Browse the repository at this point in the history
  • Loading branch information
matheusbsilva137 authored Jun 19, 2023
1 parent 71e82d1 commit 5e429d9
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 5 deletions.
6 changes: 6 additions & 0 deletions .changeset/purple-tigers-taste.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@rocket.chat/meteor": minor
"@rocket.chat/core-services": minor
---

feat: Add setting to synchronize LDAP info on OAuth logins
8 changes: 7 additions & 1 deletion apps/meteor/app/custom-oauth/server/custom_oauth_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Accounts } from 'meteor/accounts-base';
import { OAuth } from 'meteor/oauth';
import { ServiceConfiguration } from 'meteor/service-configuration';
import _ from 'underscore';
import { LDAP } from '@rocket.chat/core-services';
import { Users } from '@rocket.chat/models';
import { serverFetch as fetch } from '@rocket.chat/server-fetch';

Expand All @@ -12,6 +13,7 @@ import { Logger } from '../../logger/server';
import { isURL } from '../../../lib/utils/isURL';
import { registerAccessTokenService } from '../../lib/server/oauth/oauth';
import { callbacks } from '../../../lib/callbacks';
import { settings } from '../../settings/server';

const logger = new Logger('CustomOAuth');

Expand Down Expand Up @@ -435,11 +437,15 @@ const updateOrCreateUserFromExternalServiceAsync = async function (...args /* se
const [serviceName, serviceData] = args;

const user = updateOrCreateUserFromExternalService.apply(this, args);
const fullUser = await Users.findOneById(user.userId);
if (settings.get('LDAP_Update_Data_On_OAuth_Login')) {
await LDAP.loginAuthenticatedUserRequest(fullUser.username);
}

await callbacks.run('afterValidateNewOAuthUser', {
identity: serviceData,
serviceName,
user: await Users.findOneById(user.userId),
user: fullUser,
});

return user;
Expand Down
1 change: 1 addition & 0 deletions apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -2864,6 +2864,7 @@
"LDAP_Name_Field": "Name Field",
"LDAP_Email_Field": "Email Field",
"LDAP_Update_Data_On_Login": "Update User Data on Login",
"LDAP_Update_Data_On_OAuth_Login": "Update User Data on Login with OAuth services",
"LDAP_Advanced_Sync": "Advanced Sync",
"LDAP_Authentication": "Enable",
"LDAP_Authentication_Password": "Password",
Expand Down
74 changes: 70 additions & 4 deletions apps/meteor/server/lib/ldap/Manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,42 @@ export class LDAPManager {
return await this.loginExistingUser(ldap, user, ldapUser, password);
}

return await this.loginNewUserFromLDAP(slugifiedUsername, password, ldapUser, ldap);
return await this.loginNewUserFromLDAP(slugifiedUsername, ldap, ldapUser, password);
} finally {
ldap.disconnect();
}
}

public static async loginAuthenticatedUser(username: string): Promise<LDAPLoginResult> {
logger.debug({ msg: 'Init LDAP login', username });

if (settings.get('LDAP_Enable') !== true) {
return;
}

let ldapUser: ILDAPEntry | undefined;

const ldap = new LDAPConnection();
try {
try {
await ldap.connect();
ldapUser = await this.findAuthenticatedUser(ldap, username);
} catch (error) {
logger.error(error);
}

if (ldapUser === undefined) {
return;
}

const slugifiedUsername = this.slugifyUsername(ldapUser, username);
const user = await this.findExistingUser(ldapUser, slugifiedUsername);

if (user) {
return await this.loginExistingUser(ldap, user, ldapUser);
}

return await this.loginNewUserFromLDAP(slugifiedUsername, ldap, ldapUser);
} finally {
ldap.disconnect();
}
Expand Down Expand Up @@ -187,11 +222,42 @@ export class LDAPManager {
}
}

private static async findAuthenticatedUser(ldap: LDAPConnection, username: string): Promise<ILDAPEntry | undefined> {
const escapedUsername = ldapEscape.filter`${username}`;

try {
const users = await ldap.searchByUsername(escapedUsername);

if (users.length !== 1) {
logger.debug(`Search returned ${users.length} records for ${escapedUsername}`);
return;
}

const [ldapUser] = users;

if (settings.get<boolean>('LDAP_Find_User_After_Login')) {
// Do a search as the user and check if they have any result
authLogger.debug('User authenticated successfully, performing additional search.');
if ((await ldap.searchAndCount(ldapUser.dn, {})) === 0) {
authLogger.debug(`Bind successful but user ${ldapUser.dn} was not found via search`);
}
}

if (!(await ldap.isUserAcceptedByGroupFilter(escapedUsername, ldapUser.dn))) {
throw new Error('User not in a valid group');
}

return ldapUser;
} catch (error) {
logger.error(error);
}
}

private static async loginNewUserFromLDAP(
slugifiedUsername: string,
ldapPass: string,
ldapUser: ILDAPEntry,
ldap: LDAPConnection,
ldapUser: ILDAPEntry,
ldapPass?: string,
): Promise<LDAPLoginResult> {
logger.debug({ msg: 'User does not exist, creating', username: slugifiedUsername });

Expand Down Expand Up @@ -244,7 +310,7 @@ export class LDAPManager {
ldap: LDAPConnection,
user: IUser,
ldapUser: ILDAPEntry,
password: string,
password?: string,
): Promise<LDAPLoginResult> {
if (user.ldap !== true && settings.get('LDAP_Merge_Existing_Users') !== true) {
logger.debug('User exists without "ldap: true"');
Expand Down
4 changes: 4 additions & 0 deletions apps/meteor/server/services/ldap/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ export class LDAPService extends ServiceClassInternal implements ILDAPService {
return LDAPManager.login(username, password);
}

async loginAuthenticatedUserRequest(username: string): Promise<LDAPLoginResult> {
return LDAPManager.loginAuthenticatedUser(username);
}

async testConnection(): Promise<void> {
return LDAPManager.testConnection();
}
Expand Down
5 changes: 5 additions & 0 deletions apps/meteor/server/settings/ldap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,11 @@ export const createLdapSettings = () =>
enableQuery,
});

await this.add('LDAP_Update_Data_On_OAuth_Login', false, {
type: 'boolean',
enableQuery: [enableQuery, { _id: 'LDAP_Update_Data_On_Login', value: true }],
});

await this.add('LDAP_Default_Domain', '', {
type: 'string',
enableQuery,
Expand Down
1 change: 1 addition & 0 deletions packages/core-services/src/types/ILDAPService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { LDAPLoginResult } from '@rocket.chat/core-typings';

export interface ILDAPService {
loginRequest(username: string, password: string): Promise<LDAPLoginResult>;
loginAuthenticatedUserRequest(username: string): Promise<LDAPLoginResult>;
testConnection(): Promise<void>;
testSearch(username: string): Promise<void>;
}

0 comments on commit 5e429d9

Please sign in to comment.