Skip to content

Commit 3c87a68

Browse files
authored
feat: LDAP Sync now endpoint to sync ABAC attributes (RocketChat#40095)
1 parent e74a6bd commit 3c87a68

6 files changed

Lines changed: 49 additions & 59 deletions

File tree

.changeset/famous-socks-clap.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@rocket.chat/meteor': patch
3+
---
4+
5+
LDAP `sync now` action now syncs user's abac attributes too.

apps/meteor/ee/server/api/ldap.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ API.v1.addRoute(
2626
}
2727

2828
await LDAPEE.sync();
29-
await LDAPEE.syncAvatars();
29+
await LDAPEE.syncAvatarAndAbacAttributes();
3030

3131
return API.v1.success({
3232
message: 'Sync_in_progress' as const,

apps/meteor/ee/server/lib/ldap/Manager.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,43 @@ export class LDAPEEManager extends LDAPManager {
197197
}
198198
}
199199

200+
public static async syncAvatarAndAbacAttributes(): Promise<void> {
201+
const syncAvatars = settings.get('LDAP_Background_Sync_Avatars');
202+
const syncAbac = settings.get('LDAP_Background_Sync_ABAC_Attributes') && License.hasModule('abac') && settings.get('ABAC_Enabled');
203+
const abacMapping = syncAbac && this.parseJson(settings.get('LDAP_ABAC_AttributeMap'));
204+
205+
if (!syncAvatars && !syncAbac) {
206+
return;
207+
}
208+
209+
try {
210+
const ldap = new LDAPConnection();
211+
await ldap.connect();
212+
213+
try {
214+
const users = Users.findLDAPUsers();
215+
for await (const user of users) {
216+
const ldapUser = await this.findLDAPUser(ldap, user);
217+
if (!ldapUser) {
218+
continue;
219+
}
220+
221+
if (syncAvatars) {
222+
await LDAPManager.syncUserAvatar(user, ldapUser);
223+
}
224+
225+
if (syncAbac && abacMapping) {
226+
await Abac.addSubjectAttributes(user, ldapUser, abacMapping, undefined);
227+
}
228+
}
229+
} finally {
230+
ldap.disconnect();
231+
}
232+
} catch (err) {
233+
logger.error({ err });
234+
}
235+
}
236+
200237
public static async syncLogout(): Promise<void> {
201238
if (settings.get('LDAP_Enable') !== true || settings.get('LDAP_Sync_AutoLogout_Enabled') !== true) {
202239
return;

apps/meteor/ee/server/local-services/ldap/service.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ export class LDAPEEService extends ServiceClassInternal implements ILDAPEEServic
1616
return LDAPEEManager.syncAvatars();
1717
}
1818

19+
async syncAvatarAndAbacAttributes(): Promise<void> {
20+
return LDAPEEManager.syncAvatarAndAbacAttributes();
21+
}
22+
1923
async syncLogout(): Promise<void> {
2024
return LDAPEEManager.syncLogout();
2125
}

apps/meteor/ee/server/sdk/types/ILDAPEEService.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { FindCursor } from 'mongodb';
44
export interface ILDAPEEService {
55
sync(): Promise<void>;
66
syncAvatars(): Promise<void>;
7+
syncAvatarAndAbacAttributes(): Promise<void>;
78
syncLogout(): Promise<void>;
89
syncAbacAttributes(): Promise<void>;
910
syncUsersAbacAttributes(users: FindCursor<IUser>): Promise<void>;

apps/meteor/tests/end-to-end/api/abac.ts

Lines changed: 1 addition & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2317,17 +2317,9 @@ const addAbacAttributesToUserDirectly = async (userId: string, abacAttributes: I
23172317

23182318
before(async function () {
23192319
this.timeout(10000);
2320-
// Wait for background sync to run once before tests start
2320+
// ldap.syncNow now also syncs ABAC attributes for all users
23212321
await request.post(`${v1}/ldap.syncNow`).set(credentials);
23222322
await sleep(5000);
2323-
2324-
// Force abac attribute sync for user john.young, that way we test it too :p
2325-
await request
2326-
.post(`${v1}/abac/users/sync`)
2327-
.set(credentials)
2328-
.send({ emails: ['john.young@space.air'] });
2329-
2330-
await sleep(2000);
23312323
});
23322324

23332325
it('should sync LDAP user john.young with mapped ABAC attributes', async () => {
@@ -2347,55 +2339,6 @@ const addAbacAttributesToUserDirectly = async (userId: string, abacAttributes: I
23472339
});
23482340

23492341
it('should sync ABAC attributes for SOME users via /abac/users/sync', async () => {
2350-
// Users already imported from LDAP, but without ABAC attributes.
2351-
// We now sync only SOME users, identified by their emails.
2352-
const resAlan = await request.get(`${v1}/users.info`).set(credentials).query({ username: 'alan.bean' }).expect(200);
2353-
const resBuzz = await request.get(`${v1}/users.info`).set(credentials).query({ username: 'buzz.aldrin' }).expect(200);
2354-
2355-
const alanBefore = resAlan.body.user as IUser;
2356-
const buzzBefore = resBuzz.body.user as IUser;
2357-
2358-
// Ensure they start without ABAC attributes (or with an empty array)
2359-
expect(alanBefore).to.have.property('username', 'alan.bean');
2360-
const alanBeforeAttrs = alanBefore.abacAttributes || [];
2361-
expect(alanBeforeAttrs).to.be.an('array').that.has.lengthOf(0);
2362-
2363-
expect(buzzBefore).to.have.property('username', 'buzz.aldrin');
2364-
const buzzBeforeAttrs = buzzBefore.abacAttributes || [];
2365-
expect(buzzBeforeAttrs).to.be.an('array').that.has.lengthOf(0);
2366-
2367-
// Sync SOME users by email
2368-
await request
2369-
.post(`${v1}/abac/users/sync`)
2370-
.set(credentials)
2371-
.send({
2372-
emails: ['alan.bean@space.air', 'buzz.aldrin@space.air'],
2373-
})
2374-
.expect(200);
2375-
2376-
const resAlanAfter = await request.get(`${v1}/users.info`).set(credentials).query({ username: 'alan.bean' }).expect(200);
2377-
const resBuzzAfter = await request.get(`${v1}/users.info`).set(credentials).query({ username: 'buzz.aldrin' }).expect(200);
2378-
2379-
const alanAfter = resAlanAfter.body.user as IUser;
2380-
const buzzAfter = resBuzzAfter.body.user as IUser;
2381-
2382-
const alanAfterAttrs = alanAfter.abacAttributes || [];
2383-
const buzzAfterAttrs = buzzAfter.abacAttributes || [];
2384-
2385-
expect(alanAfterAttrs).to.be.an('array').that.is.not.empty;
2386-
expect(buzzAfterAttrs).to.be.an('array').that.is.not.empty;
2387-
2388-
const alanDept = alanAfterAttrs.find((attr: IAbacAttributeDefinition) => attr.key === 'department');
2389-
const buzzDept = buzzAfterAttrs.find((attr: IAbacAttributeDefinition) => attr.key === 'department');
2390-
2391-
expect(alanDept).to.exist;
2392-
expect(alanDept?.values || []).to.be.an('array').that.is.not.empty;
2393-
2394-
expect(buzzDept).to.exist;
2395-
expect(buzzDept?.values || []).to.be.an('array').that.is.not.empty;
2396-
});
2397-
2398-
it('should support /abac/users/sync with usernames as param', async () => {
23992342
await request
24002343
.post(`${v1}/abac/users/sync`)
24012344
.set(credentials)

0 commit comments

Comments
 (0)