Skip to content

Commit

Permalink
introduce Roles bridge (#653)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dnouv authored Aug 7, 2023
1 parent b2aafd2 commit a6eefe2
Show file tree
Hide file tree
Showing 17 changed files with 151 additions and 7 deletions.
3 changes: 3 additions & 0 deletions src/definition/accessors/IRead.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { IMessageRead } from './IMessageRead';
import { INotifier } from './INotifier';
import { IOAuthAppsReader } from './IOAuthAppsReader';
import { IPersistenceRead } from './IPersistenceRead';
import { IRoleRead } from './IRoleRead';
import { IRoomRead } from './IRoomRead';
import { IThreadRead } from './IThreadRead';
import { IUploadRead } from './IUploadRead';
Expand Down Expand Up @@ -46,4 +47,6 @@ export interface IRead {
getVideoConferenceReader(): IVideoConferenceRead;

getOAuthAppsReader(): IOAuthAppsReader;

getRoleReader(): IRoleRead;
}
27 changes: 27 additions & 0 deletions src/definition/accessors/IRoleRead.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { IRole } from '../roles';

/**
* Interface for reading roles.
*/
export interface IRoleRead {
/**
* Retrieves a role by its id or name.
* @param idOrName The id or name of the role to retrieve.
* @param appId The id of the app.
* @returns The role, if found.
* @returns null if no role is found.
* @throws If there is an error while retrieving the role.
*/
getOneByIdOrName(idOrName: IRole['id'] | IRole['name'], appId: string): Promise<IRole | null>;

/**
* Retrieves all custom roles.
* @param appId The id of the app.
* @returns All custom roles.
* @throws If there is an error while retrieving the roles.
* @throws If the app does not have the necessary permissions.
* @see IRole.protected
* @see AppPermissions.role.read
*/
getCustomRoles(appId: string): Promise<Array<IRole>>;
}
1 change: 1 addition & 0 deletions src/definition/accessors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export * from './INotifier';
export * from './IPersistence';
export * from './IPersistenceRead';
export * from './IRead';
export * from './IRoleRead';
export * from './IRoomBuilder';
export * from './IRoomExtender';
export * from './IRoomRead';
Expand Down
2 changes: 1 addition & 1 deletion src/definition/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@rocket.chat/apps-ts-definition",
"version": "1.40.0-alpha",
"version": "1.41.0-alpha",
"description": "Contains the TypeScript definitions for the Rocket.Chat Applications.",
"main": "index.js",
"typings": "index",
Expand Down
8 changes: 8 additions & 0 deletions src/definition/roles/IRole.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export interface IRole {
description: string;
mandatory2fa?: boolean;
name: string;
protected: boolean;
scope: 'Users' | 'Subscriptions';
id: string;
}
5 changes: 5 additions & 0 deletions src/definition/roles/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* This module exports the IRole interface for defining roles in Rocket.Chat Apps.
* @module definition/roles
*/
export * from './IRole';
6 changes: 6 additions & 0 deletions src/server/accessors/Reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
IVideoConferenceRead,
} from '../../definition/accessors';
import { IOAuthAppsReader } from '../../definition/accessors/IOAuthAppsReader';
import { IRoleRead } from '../../definition/accessors/IRoleRead';
import { IThreadRead } from '../../definition/accessors/IThreadRead';

export class Reader implements IRead {
Expand All @@ -29,6 +30,7 @@ export class Reader implements IRead {

private oauthApps: IOAuthAppsReader,
private thread: IThreadRead,
private role: IRoleRead,
) {}
public getEnvironmentReader(): IEnvironmentRead {
return this.env;
Expand Down Expand Up @@ -77,4 +79,8 @@ export class Reader implements IRead {
public getOAuthAppsReader(): IOAuthAppsReader {
return this.oauthApps;
}

public getRoleReader(): IRoleRead {
return this.role;
}
}
15 changes: 15 additions & 0 deletions src/server/accessors/RoleRead.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { IRoleRead } from '../../definition/accessors/IRoleRead';
import { IRole } from '../../definition/roles';
import { RoleBridge } from '../bridges';

export class RoleRead implements IRoleRead {
constructor(private roleBridge: RoleBridge, private appId: string) {}

public getOneByIdOrName(idOrName: IRole['id'] | IRole['name']): Promise<IRole | null> {
return this.roleBridge.doGetOneByIdOrName(idOrName, this.appId);
}

public getCustomRoles(): Promise<Array<IRole>> {
return this.roleBridge.doGetCustomRoles(this.appId);
}
}
2 changes: 2 additions & 0 deletions src/server/accessors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { OAuthAppsReader } from './OAuthAppsReader';
import { Persistence } from './Persistence';
import { PersistenceRead } from './PersistenceRead';
import { Reader } from './Reader';
import { RoleRead } from './RoleRead';
import { RoomBuilder } from './RoomBuilder';
import { RoomExtender } from './RoomExtender';
import { RoomRead } from './RoomRead';
Expand Down Expand Up @@ -68,6 +69,7 @@ export {
Persistence,
PersistenceRead,
Reader,
RoleRead,
RoomBuilder,
RoomExtender,
RoomRead,
Expand Down
6 changes: 5 additions & 1 deletion src/server/bridges/AppBridges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { MessageBridge } from './MessageBridge';
import { ModerationBridge } from './ModerationBridge';
import { OAuthAppsBridge } from './OAuthAppsBridge';
import { PersistenceBridge } from './PersistenceBridge';
import { RoleBridge } from './RoleBridge';
import { RoomBridge } from './RoomBridge';
import { SchedulerBridge } from './SchedulerBridge';
import { ServerSettingBridge } from './ServerSettingBridge';
Expand Down Expand Up @@ -42,7 +43,8 @@ export type Bridge =
| SchedulerBridge
| VideoConferenceBridge
| OAuthAppsBridge
| ModerationBridge;
| ModerationBridge
| RoleBridge;

export abstract class AppBridges {
public abstract getCommandBridge(): CommandBridge;
Expand All @@ -69,4 +71,6 @@ export abstract class AppBridges {
public abstract getModerationBridge(): ModerationBridge;

public abstract getThreadBridge(): ThreadBridge;

public abstract getRoleBridge(): RoleBridge;
}
37 changes: 37 additions & 0 deletions src/server/bridges/RoleBridge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { IRole } from '../../definition/roles';
import { PermissionDeniedError } from '../errors/PermissionDeniedError';
import { AppPermissionManager } from '../managers/AppPermissionManager';
import { AppPermissions } from '../permissions/AppPermissions';
import { BaseBridge } from './BaseBridge';

export abstract class RoleBridge extends BaseBridge {
public async doGetOneByIdOrName(idOrName: IRole['id'] | IRole['name'], appId: string): Promise<IRole | null> {
if (this.hasReadPermission(appId)) {
return this.getOneByIdOrName(idOrName, appId);
}
}

public async doGetCustomRoles(appId: string): Promise<Array<IRole>> {
if (this.hasReadPermission(appId)) {
return this.getCustomRoles(appId);
}
}

protected abstract getOneByIdOrName(idOrName: IRole['id'] | IRole['name'], appId: string): Promise<IRole | null>;
protected abstract getCustomRoles(appId: string): Promise<Array<IRole>>;

private hasReadPermission(appId: string): boolean {
if (AppPermissionManager.hasPermission(appId, AppPermissions.role.read)) {
return true;
}

AppPermissionManager.notifyAboutError(
new PermissionDeniedError({
appId,
missingPermissions: [AppPermissions.role.read],
}),
);

return false;
}
}
2 changes: 2 additions & 0 deletions src/server/bridges/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { LivechatBridge } from './LivechatBridge';
import { MessageBridge } from './MessageBridge';
import { ModerationBridge } from './ModerationBridge';
import { PersistenceBridge } from './PersistenceBridge';
import { RoleBridge } from './RoleBridge';
import { RoomBridge } from './RoomBridge';
import { SchedulerBridge } from './SchedulerBridge';
import { ServerSettingBridge } from './ServerSettingBridge';
Expand Down Expand Up @@ -45,4 +46,5 @@ export {
VideoConferenceBridge,
IInternalFederationBridge,
ModerationBridge,
RoleBridge,
};
5 changes: 4 additions & 1 deletion src/server/managers/AppAccessorManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
Persistence,
PersistenceRead,
Reader,
RoleRead,
RoomRead,
SchedulerExtend,
SchedulerModify,
Expand Down Expand Up @@ -177,7 +178,9 @@ export class AppAccessorManager {

const thread = new ThreadRead(this.bridges.getThreadBridge(), appId);

this.readers.set(appId, new Reader(env, msg, persist, room, user, noti, livechat, upload, cloud, videoConf, oauthApps, thread));
const role = new RoleRead(this.bridges.getRoleBridge(), appId);

this.readers.set(appId, new Reader(env, msg, persist, room, user, noti, livechat, upload, cloud, videoConf, oauthApps, thread, role));
}

return this.readers.get(appId);
Expand Down
14 changes: 10 additions & 4 deletions src/server/permissions/AppPermissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,18 @@ export const AppPermissions = {
read: { name: 'room.read' },
write: { name: 'room.write' },
},
'role': {
read: { name: 'role.read' },
write: { name: 'role.write' },
},
'message': {
read: { name: 'message.read' },
write: { name: 'message.write' },
},
'moderation': {
read: { name: 'moderation.read' },
write: { name: 'moderation.write' },
},
'threads': {
read: { name: 'threads.read' },
},
Expand Down Expand Up @@ -99,10 +107,6 @@ export const AppPermissions = {
read: { name: 'oauth-app.read' },
write: { name: 'oauth-app.write' },
},
'moderation': {
read: { name: 'moderation.read' },
write: { name: 'moderation.write' },
},
};

export const defaultPermissions: Array<IPermission> = [
Expand Down Expand Up @@ -138,4 +142,6 @@ export const defaultPermissions: Array<IPermission> = [
AppPermissions.apis.default,
AppPermissions.moderation.read,
AppPermissions.moderation.write,
AppPermissions.role.read,
AppPermissions.role.write,
];
6 changes: 6 additions & 0 deletions tests/server/accessors/Reader.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
IMessageRead,
INotifier,
IPersistenceRead,
IRoleRead,
IRoomRead,
IUploadRead,
IUserRead,
Expand All @@ -29,6 +30,7 @@ export class ReaderAccessorTestFixture {
private oauthApps: IOAuthAppsReader;

private thread: IThreadRead;
private role: IRoleRead;

@SetupFixture
public setupFixture() {
Expand All @@ -44,6 +46,7 @@ export class ReaderAccessorTestFixture {
this.videoConf = {} as IVideoConferenceRead;
this.oauthApps = {} as IOAuthAppsReader;
this.thread = {} as IThreadRead;
this.role = {} as IRoleRead;
}

@Test()
Expand All @@ -63,6 +66,7 @@ export class ReaderAccessorTestFixture {
this.videoConf,
this.oauthApps,
this.thread,
this.role,
),
).not.toThrow();

Expand All @@ -79,6 +83,7 @@ export class ReaderAccessorTestFixture {
this.videoConf,
this.oauthApps,
this.thread,
this.role,
);

Expect(rd.getEnvironmentReader()).toBeDefined();
Expand All @@ -90,5 +95,6 @@ export class ReaderAccessorTestFixture {
Expect(rd.getLivechatReader()).toBeDefined();
Expect(rd.getUploadReader()).toBeDefined();
Expect(rd.getVideoConferenceReader()).toBeDefined();
Expect(rd.getRoleReader()).toBeDefined();
}
}
7 changes: 7 additions & 0 deletions tests/test-data/bridges/appBridges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
MessageBridge,
ModerationBridge,
PersistenceBridge,
RoleBridge,
RoomBridge,
SchedulerBridge,
ServerSettingBridge,
Expand All @@ -36,6 +37,7 @@ import { TestsMessageBridge } from './messageBridge';
import { TestsModerationBridge } from './moderationBridge';
import { TestOAuthAppsBridge } from './OAuthAppsBridge';
import { TestsPersisBridge } from './persisBridge';
import { TestsRoleBridge } from './roleBridge';
import { TestsRoomBridge } from './roomBridge';
import { TestSchedulerBridge } from './schedulerBridge';
import { TestsServerSettingBridge } from './serverSettingBridge';
Expand All @@ -55,6 +57,7 @@ export class TestsAppBridges extends AppBridges {
private readonly msgBridge: TestsMessageBridge;
private readonly moderationBridge: TestsModerationBridge;
private readonly persisBridge: TestsPersisBridge;
private readonly roleBridge: TestsRoleBridge;
private readonly roomBridge: TestsRoomBridge;
private readonly internalBridge: TestsInternalBridge;
private readonly userBridge: TestsUserBridge;
Expand All @@ -81,6 +84,7 @@ export class TestsAppBridges extends AppBridges {
this.msgBridge = new TestsMessageBridge();
this.moderationBridge = new TestsModerationBridge();
this.persisBridge = new TestsPersisBridge();
this.roleBridge = new TestsRoleBridge();
this.roomBridge = new TestsRoomBridge();
this.internalBridge = new TestsInternalBridge();
this.userBridge = new TestsUserBridge();
Expand Down Expand Up @@ -143,6 +147,9 @@ export class TestsAppBridges extends AppBridges {
public getThreadBridge(): ThreadBridge {
return this.threadBridge;
}
public getRoleBridge(): RoleBridge {
return this.roleBridge;
}

public getRoomBridge(): RoomBridge {
return this.roomBridge;
Expand Down
12 changes: 12 additions & 0 deletions tests/test-data/bridges/roleBridge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { IRole } from '../../../src/definition/roles';
import { RoleBridge } from '../../../src/server/bridges';

export class TestsRoleBridge extends RoleBridge {
public getOneByIdOrName(idOrName: IRole['id'] | IRole['name'], appId: string): Promise<IRole | null> {
throw new Error('Method not implemented.');
}

public getCustomRoles(appId: string): Promise<Array<IRole>> {
throw new Error('Method not implemented.');
}
}

0 comments on commit a6eefe2

Please sign in to comment.