From 7fcf5fe68ae2555fb75085a77962ffe55da3ac5c Mon Sep 17 00:00:00 2001 From: smirk Date: Sun, 22 Feb 2026 05:33:43 +0000 Subject: [PATCH 1/5] refactor: migrate ldap endpoints to new chained API pattern Migrates ldap.testConnection and ldap.testSearch POST endpoints from the legacy addRoute() pattern to the new chained .post() API pattern with typed AJV response schemas. Replaces Meteor check() with isLdapTestSearch AJV validator for request body validation on ldap.testSearch. Part of #38876 --- .../refactor-ldap-api-chained-pattern.md | 5 + apps/meteor/app/api/server/v1/ldap.ts | 118 +++++++++++------- 2 files changed, 78 insertions(+), 45 deletions(-) create mode 100644 .changeset/refactor-ldap-api-chained-pattern.md diff --git a/.changeset/refactor-ldap-api-chained-pattern.md b/.changeset/refactor-ldap-api-chained-pattern.md new file mode 100644 index 0000000000000..bd765df2328d0 --- /dev/null +++ b/.changeset/refactor-ldap-api-chained-pattern.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Migrated `ldap.testConnection` and `ldap.testSearch` REST API endpoints from legacy `addRoute` pattern to the new chained `.post()` API pattern with typed response schemas and AJV body validation (replacing Meteor `check()`). diff --git a/apps/meteor/app/api/server/v1/ldap.ts b/apps/meteor/app/api/server/v1/ldap.ts index 3f9a2c29deded..0309768116c71 100644 --- a/apps/meteor/app/api/server/v1/ldap.ts +++ b/apps/meteor/app/api/server/v1/ldap.ts @@ -1,62 +1,90 @@ import { LDAP } from '@rocket.chat/core-services'; -import { Match, check } from 'meteor/check'; +import { ajv, validateUnauthorizedErrorResponse, validateForbiddenErrorResponse } from '@rocket.chat/rest-typings'; import { SystemLogger } from '../../../../server/lib/logger/system'; import { settings } from '../../../settings/server'; import { API } from '../api'; -API.v1.addRoute( +const messageResponseSchema = { + type: 'object' as const, + properties: { + message: { type: 'string' as const }, + success: { + type: 'boolean' as const, + enum: [true] as const, + }, + }, + required: ['message', 'success'] as const, + additionalProperties: false, +}; + +const isLdapTestSearch = ajv.compile<{ username: string }>({ + type: 'object', + properties: { + username: { type: 'string' }, + }, + required: ['username'], + additionalProperties: false, +}); + +API.v1.post( 'ldap.testConnection', - { authRequired: true, permissionsRequired: ['test-admin-options'] }, { - async post() { - if (!this.userId) { - throw new Error('error-invalid-user'); - } - - if (settings.get('LDAP_Enable') !== true) { - throw new Error('LDAP_disabled'); - } - - try { - await LDAP.testConnection(); - } catch (err) { - SystemLogger.error({ err }); - throw new Error('Connection_failed'); - } - - return API.v1.success({ - message: 'LDAP_Connection_successful' as const, - }); + authRequired: true, + permissionsRequired: ['test-admin-options'], + response: { + 200: ajv.compile<{ message: string }>(messageResponseSchema), + 401: validateUnauthorizedErrorResponse, + 403: validateForbiddenErrorResponse, }, }, + async function action() { + if (!this.userId) { + throw new Error('error-invalid-user'); + } + + if (settings.get('LDAP_Enable') !== true) { + throw new Error('LDAP_disabled'); + } + + try { + await LDAP.testConnection(); + } catch (err) { + SystemLogger.error({ err }); + throw new Error('Connection_failed'); + } + + return API.v1.success({ + message: 'LDAP_Connection_successful' as const, + }); + }, ); -API.v1.addRoute( +API.v1.post( 'ldap.testSearch', - { authRequired: true, permissionsRequired: ['test-admin-options'] }, { - async post() { - check( - this.bodyParams, - Match.ObjectIncluding({ - username: String, - }), - ); - - if (!this.userId) { - throw new Error('error-invalid-user'); - } - - if (settings.get('LDAP_Enable') !== true) { - throw new Error('LDAP_disabled'); - } - - await LDAP.testSearch(this.bodyParams.username); - - return API.v1.success({ - message: 'LDAP_User_Found' as const, - }); + authRequired: true, + permissionsRequired: ['test-admin-options'], + body: isLdapTestSearch, + response: { + 200: ajv.compile<{ message: string }>(messageResponseSchema), + 401: validateUnauthorizedErrorResponse, + 403: validateForbiddenErrorResponse, }, }, + async function action() { + if (!this.userId) { + throw new Error('error-invalid-user'); + } + + if (settings.get('LDAP_Enable') !== true) { + throw new Error('LDAP_disabled'); + } + + await LDAP.testSearch(this.bodyParams.username); + + return API.v1.success({ + message: 'LDAP_User_Found' as const, + }); + }, ); From bdda04597c5fab116bad6c13c4a3e86080da9326 Mon Sep 17 00:00:00 2001 From: smirk Date: Tue, 24 Feb 2026 09:28:31 +0000 Subject: [PATCH 2/5] fix: address CodeRabbit review comments - Import isLdapTestSearch from rest-typings instead of redefining locally - Add success: true to ajv.compile generic types - Add try/catch error handling to ldap.testSearch with SystemLogger - Add type param to settings.get for LDAP_Enable --- apps/meteor/app/api/server/v1/ldap.ts | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/apps/meteor/app/api/server/v1/ldap.ts b/apps/meteor/app/api/server/v1/ldap.ts index 0309768116c71..efbf9a898d02c 100644 --- a/apps/meteor/app/api/server/v1/ldap.ts +++ b/apps/meteor/app/api/server/v1/ldap.ts @@ -1,5 +1,5 @@ import { LDAP } from '@rocket.chat/core-services'; -import { ajv, validateUnauthorizedErrorResponse, validateForbiddenErrorResponse } from '@rocket.chat/rest-typings'; +import { ajv, isLdapTestSearch, validateUnauthorizedErrorResponse, validateForbiddenErrorResponse } from '@rocket.chat/rest-typings'; import { SystemLogger } from '../../../../server/lib/logger/system'; import { settings } from '../../../settings/server'; @@ -18,22 +18,13 @@ const messageResponseSchema = { additionalProperties: false, }; -const isLdapTestSearch = ajv.compile<{ username: string }>({ - type: 'object', - properties: { - username: { type: 'string' }, - }, - required: ['username'], - additionalProperties: false, -}); - API.v1.post( 'ldap.testConnection', { authRequired: true, permissionsRequired: ['test-admin-options'], response: { - 200: ajv.compile<{ message: string }>(messageResponseSchema), + 200: ajv.compile<{ message: string; success: true }>(messageResponseSchema), 401: validateUnauthorizedErrorResponse, 403: validateForbiddenErrorResponse, }, @@ -67,7 +58,7 @@ API.v1.post( permissionsRequired: ['test-admin-options'], body: isLdapTestSearch, response: { - 200: ajv.compile<{ message: string }>(messageResponseSchema), + 200: ajv.compile<{ message: string; success: true }>(messageResponseSchema), 401: validateUnauthorizedErrorResponse, 403: validateForbiddenErrorResponse, }, @@ -77,11 +68,16 @@ API.v1.post( throw new Error('error-invalid-user'); } - if (settings.get('LDAP_Enable') !== true) { + if (settings.get('LDAP_Enable') !== true) { throw new Error('LDAP_disabled'); } - await LDAP.testSearch(this.bodyParams.username); + try { + await LDAP.testSearch(this.bodyParams.username); + } catch (err) { + SystemLogger.error({ err }); + throw new Error('LDAP_search_failed'); + } return API.v1.success({ message: 'LDAP_User_Found' as const, From 4f7f8af7feb5a63c2e1d672d02f267cfc1438243 Mon Sep 17 00:00:00 2001 From: smirk Date: Tue, 24 Feb 2026 09:42:55 +0000 Subject: [PATCH 3/5] fix: export isLdapTestSearch from rest-typings --- packages/rest-typings/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/rest-typings/src/index.ts b/packages/rest-typings/src/index.ts index 097ce5081b1de..9f8a6a517ec8d 100644 --- a/packages/rest-typings/src/index.ts +++ b/packages/rest-typings/src/index.ts @@ -229,6 +229,7 @@ export * from './helpers/ReplacePlaceholders'; export * from './helpers/WithItemCount'; export * from './v1/emojiCustom'; export * from './v1/instances'; +export * from './v1/ldap'; export * from './v1/users'; export * from './v1/users/UsersSetAvatarParamsPOST'; export * from './v1/users/UsersSetPreferenceParamsPOST'; From 1cd0b4ee49d2efe56d4cd4b24938eb088fd7b2f7 Mon Sep 17 00:00:00 2001 From: Suryansh Mishra Date: Wed, 25 Feb 2026 22:47:45 +0530 Subject: [PATCH 4/5] Update .changeset/refactor-ldap-api-chained-pattern.md Co-authored-by: Guilherme Gazzo --- .changeset/refactor-ldap-api-chained-pattern.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/refactor-ldap-api-chained-pattern.md b/.changeset/refactor-ldap-api-chained-pattern.md index bd765df2328d0..72786ca6c7390 100644 --- a/.changeset/refactor-ldap-api-chained-pattern.md +++ b/.changeset/refactor-ldap-api-chained-pattern.md @@ -2,4 +2,4 @@ '@rocket.chat/meteor': patch --- -Migrated `ldap.testConnection` and `ldap.testSearch` REST API endpoints from legacy `addRoute` pattern to the new chained `.post()` API pattern with typed response schemas and AJV body validation (replacing Meteor `check()`). +Migrates `ldap.testConnection` and `ldap.testSearch` REST API endpoints from legacy `addRoute` pattern to the new chained `.post()` API pattern with typed response schemas and AJV body validation (replacing Meteor `check()`). From 24b5ea0aa390cac5f9e4d9e5a301d9ba7e329267 Mon Sep 17 00:00:00 2001 From: Suryansh Mishra Date: Wed, 25 Feb 2026 22:48:14 +0530 Subject: [PATCH 5/5] Update .changeset/refactor-ldap-api-chained-pattern.md Co-authored-by: Guilherme Gazzo --- .changeset/refactor-ldap-api-chained-pattern.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/refactor-ldap-api-chained-pattern.md b/.changeset/refactor-ldap-api-chained-pattern.md index 72786ca6c7390..e402e8609cb46 100644 --- a/.changeset/refactor-ldap-api-chained-pattern.md +++ b/.changeset/refactor-ldap-api-chained-pattern.md @@ -1,5 +1,5 @@ --- -'@rocket.chat/meteor': patch +'@rocket.chat/meteor': minor --- Migrates `ldap.testConnection` and `ldap.testSearch` REST API endpoints from legacy `addRoute` pattern to the new chained `.post()` API pattern with typed response schemas and AJV body validation (replacing Meteor `check()`).