Skip to content

Commit

Permalink
add fix errors, change contextReference back to ObjectId, tested loca…
Browse files Browse the repository at this point in the history
…lly => works
  • Loading branch information
EzzatOmar committed Nov 13, 2023
1 parent 634d4a9 commit 4d36830
Show file tree
Hide file tree
Showing 10 changed files with 57 additions and 33 deletions.
1 change: 1 addition & 0 deletions apps/server/src/modules/authorization/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ export {
} from './domain';
// Should not used anymore
export { FeathersAuthorizationService } from './feathers';
export { PermissionContextService } from './permission-context/service/permission-context.service';
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { ObjectId } from 'bson';
import { createMock, DeepMocked } from '@golevelup/ts-jest';
import { Test, TestingModule } from '@nestjs/testing';
import { permissionContextFactory, setupEntities, userFactory } from '@shared/testing';
Expand Down Expand Up @@ -50,10 +49,10 @@ describe('PermissionContextService', () => {

it('should call permissionContextRepo', async () => {
const { user, spy } = setup();
const oid = new ObjectId();
const resolvedPermissions = await service.resolvePermissions(user, oid);
const id = 'TEST ID';
const resolvedPermissions = await service.resolvePermissions(user.id, id);
expect(resolvedPermissions).toEqual([]);
expect(spy).toBeCalledWith(oid);
expect(spy).toBeCalledWith(id);
});
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import { Injectable } from '@nestjs/common';
import { User, Permission } from '@shared/domain';
import { User, Permission, EntityId } from '@shared/domain';
import { UserRepo, PermissionContextRepo } from '@shared/repo';
import { ObjectId } from 'bson';

@Injectable()
export class PermissionContextService {
constructor(private readonly userRepo: UserRepo, private readonly permissionContextRepo: PermissionContextRepo) {}

public async resolvePermissions(user: User, contextReference: ObjectId): Promise<Permission[]> {
public async resolvePermissions(userId: User['id'], contextReference: EntityId): Promise<Permission[]> {
// NOTE: the contextReference is the _id to a collection that needs authorization
const permissionCtxEntities = await this.permissionContextRepo.findByContextReference(contextReference);

const permissions = permissionCtxEntities.resolvedPermissions(user);
const permissionCtxEntity = await this.permissionContextRepo.findByContextReference(contextReference);
const permissions = permissionCtxEntity.resolvedPermissions(userId);

return permissions;
}
Expand Down
27 changes: 21 additions & 6 deletions apps/server/src/modules/board/uc/board.uc.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { forwardRef, Inject, Injectable } from '@nestjs/common';
import { BoardExternalReference, Column, ColumnBoard, EntityId } from '@shared/domain';
import { forwardRef, Inject, Injectable, UnauthorizedException } from '@nestjs/common';
import { BoardExternalReference, Column, ColumnBoard, EntityId, Permission } from '@shared/domain';
import { LegacyLogger } from '@src/core/logger';
import { AuthorizationService } from '@modules/authorization/domain';
import { Action } from '@modules/authorization';
import { Action, PermissionContextService } from '@modules/authorization';
import { CardService, ColumnBoardService, ColumnService } from '../service';
import { BoardDoAuthorizableService } from '../service/board-do-authorizable.service';
import { BaseUc } from './base.uc';
Expand All @@ -16,26 +16,41 @@ export class BoardUc extends BaseUc {
private readonly cardService: CardService,
private readonly columnBoardService: ColumnBoardService,
private readonly columnService: ColumnService,
private readonly logger: LegacyLogger
private readonly logger: LegacyLogger,
protected readonly permissionContextService: PermissionContextService
) {
super(authorizationService, boardDoAuthorizableService);
this.logger.setContext(BoardUc.name);
}

private async pocCheckPermission(
userId: EntityId,
contextReference: EntityId,
permissionsToContain: Permission[]
): Promise<void> {
const permissions = await this.permissionContextService.resolvePermissions(userId, contextReference);
const hasPermission = permissionsToContain.every((permission) => permissions.includes(permission));
if (!hasPermission) {
throw new UnauthorizedException();
}
}

async findBoard(userId: EntityId, boardId: EntityId): Promise<ColumnBoard> {
this.logger.debug({ action: 'findBoard', userId, boardId });
await this.pocCheckPermission(userId, boardId, [Permission.BOARD_READ]);

const board = await this.columnBoardService.findById(boardId);
await this.checkPermission(userId, board, Action.read);
// await this.checkPermission(userId, board, Action.read);

return board;
}

async findBoardContext(userId: EntityId, boardId: EntityId): Promise<BoardExternalReference> {
this.logger.debug({ action: 'findBoardContext', userId, boardId });

await this.pocCheckPermission(userId, boardId, [Permission.BOARD_READ]);
const board = await this.columnBoardService.findById(boardId);
await this.checkPermission(userId, board, Action.read);
// await this.checkPermission(userId, board, Action.read);

return board.context;
}
Expand Down
2 changes: 2 additions & 0 deletions apps/server/src/shared/domain/entity/all-entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { TeamEntity, TeamUserEntity } from './team.entity';
import { UserLoginMigrationEntity } from './user-login-migration.entity';
import { User } from './user.entity';
import { VideoConference } from './video-conference.entity';
import { PermissionContextEntity } from './permission-context.entity';

export const ALL_ENTITIES = [
Account,
Expand Down Expand Up @@ -100,4 +101,5 @@ export const ALL_ENTITIES = [
UserLoginMigrationEntity,
VideoConference,
GroupEntity,
PermissionContextEntity,
];
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ describe('PermissionContextEntity Entity', () => {

it('should resolve nested permissions', () => {
const { user, permissionContext } = setup();
const resolvedPermissions = permissionContext.resolvedPermissions(user);
const resolvedPermissions = permissionContext.resolvedPermissions(user.id);
expect(resolvedPermissions.sort()).toEqual([Permission.ADD_SCHOOL_MEMBERS, Permission.ACCOUNT_EDIT].sort());
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Embeddable, Entity, Index, ManyToOne, Property } from '@mikro-orm/core';
import { ObjectId } from 'bson';
import { Embeddable, Entity, Index, ManyToOne, Property, Unique } from '@mikro-orm/core';
import { ObjectId } from '@mikro-orm/mongodb';
import { BaseEntityWithTimestamps } from './base.entity';
import { Permission } from '../interface';
import { User } from './user.entity';
Expand Down Expand Up @@ -28,11 +28,10 @@ export interface IPermissionContextProperties {
parentContext: PermissionContextEntity | null;
}

// TODO: add test
@Entity({ tableName: 'permission-context' })
export class PermissionContextEntity extends BaseEntityWithTimestamps {
@Property()
@Index()
@Unique()
contextReference: ObjectId;

@Property()
Expand All @@ -54,19 +53,19 @@ export class PermissionContextEntity extends BaseEntityWithTimestamps {
this.userDelta = props.userDelta ?? new UserDelta([]);
}

private resolveUserDelta(user: User): {
private resolveUserDelta(userId: User['id']): {
includedPermissions: Permission[];
excludedPermissions: Permission[];
} {
const userDelta = this.userDelta[user.id] ?? { includedPermissions: [], excludedPermissions: [] };
const userDelta = this.userDelta[userId] ?? { includedPermissions: [], excludedPermissions: [] };

return userDelta;
}

public resolvedPermissions(user: User): Permission[] {
const parentPermissions = this.parentContext?.resolvedPermissions(user) ?? [];
public resolvedPermissions(userId: User['id']): Permission[] {
const parentPermissions = this.parentContext?.resolvedPermissions(userId) ?? [];

const userDelta = this.resolveUserDelta(user);
const userDelta = this.resolveUserDelta(userId);

const finalPermissions = parentPermissions
.concat(userDelta.includedPermissions)
Expand Down
10 changes: 10 additions & 0 deletions apps/server/src/shared/domain/interface/permission.enum.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
// NOTE: we should remove enum and replace them with type of string
// to be able to group them and merge them into Permission type
// currently typescript does not allow merging of enums
export enum Permission {
/** POC: BOARD PERMISSIONS */
BOARD_READ = 'BOARD_READ',
BOARD_CREATE = 'BOARD_CREATE',
BOARD_CREATE_COLUMN = 'BOARD_CREATE_COLUMN',
BOARD_DELETE = 'BOARD_DELETE',
BOARD_UPDATE_TITLE = 'BOARD_UPDATE_TITLE',
/** POC END: BOARD PERMISSIONS */
ACCOUNT_CREATE = 'ACCOUNT_CREATE',
ACCOUNT_EDIT = 'ACCOUNT_EDIT',
ADD_SCHOOL_MEMBERS = 'ADD_SCHOOL_MEMBERS',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ObjectId } from 'bson';
import { ObjectId } from '@mikro-orm/mongodb';
import { Injectable } from '@nestjs/common';
import { PermissionContextEntity } from '@shared/domain';
import { EntityId, PermissionContextEntity } from '@shared/domain';
import { BaseRepo } from '../base.repo';

// TODO: add test
Expand All @@ -10,7 +10,7 @@ export class PermissionContextRepo extends BaseRepo<PermissionContextEntity> {
return PermissionContextEntity;
}

findByContextReference(contextReference: ObjectId): Promise<PermissionContextEntity> {
return this._em.findOneOrFail(PermissionContextEntity, { contextReference });
findByContextReference(contextReference: EntityId): Promise<PermissionContextEntity> {
return this._em.findOneOrFail(PermissionContextEntity, { contextReference: new ObjectId(contextReference) });
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* istanbul ignore file */
import { ObjectId } from 'bson';
import { PermissionContextEntity, IPermissionContextProperties, UserDelta } from '@shared/domain';
import { ObjectId } from '@mikro-orm/mongodb';
import { PermissionContextEntity, IPermissionContextProperties, UserDelta, EntityId } from '@shared/domain';
import { DeepPartial } from 'fishery';
import { BaseFactory } from './base.factory';

Expand All @@ -17,7 +17,7 @@ class PermissionContextFactory extends BaseFactory<PermissionContextEntity, IPer
return this.params(params);
}

withContextReference(contextReference: ObjectId): this {
withContextReference(contextReference: EntityId): this {
const params: DeepPartial<IPermissionContextProperties> = { contextReference };

return this.params(params);
Expand Down

0 comments on commit 4d36830

Please sign in to comment.