Skip to content

Commit

Permalink
guard with permissionCtx in column,card,element
Browse files Browse the repository at this point in the history
  • Loading branch information
EzzatOmar committed Nov 20, 2023
1 parent 9369247 commit 464bbc0
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 15 deletions.
14 changes: 12 additions & 2 deletions apps/server/src/modules/board/service/card.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { Card, Column, ContentElementType, EntityId } from '@shared/domain';
import { Card, Column, ContentElementType, EntityId, PermissionContextEntity } from '@shared/domain';
import { PermissionContextRepo } from '@shared/repo';
import { ObjectId } from 'bson';
import { BoardDoRepo } from '../repo';
import { BoardDoService } from './board-do.service';
Expand All @@ -10,7 +11,8 @@ export class CardService {
constructor(
private readonly boardDoRepo: BoardDoRepo,
private readonly boardDoService: BoardDoService,
private readonly contentElementService: ContentElementService
private readonly contentElementService: ContentElementService,
private readonly permissionCtxRepo: PermissionContextRepo
) {}

async findById(cardId: EntityId): Promise<Card> {
Expand Down Expand Up @@ -38,6 +40,14 @@ export class CardService {

parent.addChild(card);

const parentContext = await this.permissionCtxRepo.findByContextReference(parent.id);
const permissionCtxEntity = new PermissionContextEntity({
name: 'Card permission context',
parentContext,
contextReference: new ObjectId(card.id),
});
await this.permissionCtxRepo.save(permissionCtxEntity);

await this.boardDoRepo.save(parent.children, parent);

if (requiredEmptyElements) {
Expand Down
5 changes: 5 additions & 0 deletions apps/server/src/modules/board/service/column-board.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ const DEFAULT_TEACHER_PERMISSIONS = [

Permission.BOARD_CARD_CREATE,
Permission.BOARD_CARD_MOVE,
Permission.BOARD_CARD_DELETE,
Permission.BOARD_CARD_UPDATE,

Permission.BOARD_ELEMENT_CREATE,
Permission.BOARD_ELEMENT_MOVE,
];
const DEFAULT_SUBSTITUTE_TEACHER_PERMISSIONS = DEFAULT_TEACHER_PERMISSIONS;

Expand Down
17 changes: 15 additions & 2 deletions apps/server/src/modules/board/service/column.service.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { Injectable } from '@nestjs/common';
import { Column, ColumnBoard, EntityId } from '@shared/domain';
import { Column, ColumnBoard, EntityId, PermissionContextEntity } from '@shared/domain';
import { ObjectId } from 'bson';
import { PermissionContextRepo } from '@shared/repo';
import { BoardDoRepo } from '../repo';
import { BoardDoService } from './board-do.service';

@Injectable()
export class ColumnService {
constructor(private readonly boardDoRepo: BoardDoRepo, private readonly boardDoService: BoardDoService) {}
constructor(
private readonly boardDoRepo: BoardDoRepo,
private readonly boardDoService: BoardDoService,
private readonly permissionCtxRepo: PermissionContextRepo
) {}

async findById(columnId: EntityId): Promise<Column> {
const column = await this.boardDoRepo.findByClassAndId(Column, columnId);
Expand All @@ -24,6 +29,14 @@ export class ColumnService {

parent.addChild(column);

const parentContext = await this.permissionCtxRepo.findByContextReference(parent.id);
const permissionCtxEntity = new PermissionContextEntity({
name: 'Column permission context',
parentContext,
contextReference: new ObjectId(column.id),
});
await this.permissionCtxRepo.save(permissionCtxEntity);

await this.boardDoRepo.save(parent.children, parent);

return column;
Expand Down
26 changes: 25 additions & 1 deletion apps/server/src/modules/board/service/content-element.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import {
ContentElementType,
EntityId,
isAnyContentElement,
PermissionContextEntity,
SubmissionItem,
} from '@shared/domain';
import { PermissionContextRepo } from '@shared/repo';
import { ObjectId } from 'bson';
import { AnyElementContentBody } from '../controller/dto';
import { BoardDoRepo } from '../repo';
import { BoardDoService } from './board-do.service';
Expand All @@ -19,7 +22,8 @@ export class ContentElementService {
constructor(
private readonly boardDoRepo: BoardDoRepo,
private readonly boardDoService: BoardDoService,
private readonly contentElementFactory: ContentElementFactory
private readonly contentElementFactory: ContentElementFactory,
private readonly permissionCtxRepo: PermissionContextRepo
) {}

async findById(elementId: EntityId): Promise<AnyContentElementDo> {
Expand All @@ -40,9 +44,29 @@ export class ContentElementService {
return parent;
}

async pocCreateElementPermissionCtx(element: AnyContentElementDo, parentContext: PermissionContextEntity) {
const permissionCtxEntity = new PermissionContextEntity({
name: 'Element permission context',
parentContext,
contextReference: new ObjectId(element.id),
});
await this.permissionCtxRepo.save(permissionCtxEntity);
}

async create(parent: Card | SubmissionItem, type: ContentElementType): Promise<AnyContentElementDo> {
const element = this.contentElementFactory.build(type);
parent.addChild(element);

const parentContext = await this.permissionCtxRepo.findByContextReference(parent.id);

const permissionCtxEntity = new PermissionContextEntity({
name: 'Card permission context',
parentContext,
contextReference: new ObjectId(element.id),
});
await this.pocCreateElementPermissionCtx(element, parentContext);
await this.permissionCtxRepo.save(permissionCtxEntity);

await this.boardDoRepo.save(parent.children, parent);
return element;
}
Expand Down
36 changes: 28 additions & 8 deletions apps/server/src/modules/board/uc/card.uc.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { forwardRef, Inject, Injectable } from '@nestjs/common';
import { AnyBoardDo, AnyContentElementDo, Card, ContentElementType, EntityId } from '@shared/domain';
import { AnyBoardDo, AnyContentElementDo, Card, ContentElementType, EntityId, Permission } from '@shared/domain';
import { LegacyLogger } from '@src/core/logger';
import { AuthorizationService, Action, PermissionContextService } from '@modules/authorization';
import { BoardDoAuthorizableService, CardService, ContentElementService } from '../service';
Expand All @@ -24,34 +24,49 @@ export class CardUc extends BaseUc {
this.logger.debug({ action: 'findCards', userId, cardIds });

const cards = await this.cardService.findByIds(cardIds);
const allowedCards = await this.filterAllowed(userId, cards, Action.read);

const cardsWithPermission = await Promise.all(
cards.map(async (card) => {
const hasPermission = await this.pocHasPermission(userId, card.id, [Permission.BOARD_READ]);
return { card, hasPermission };
})
);

const allowedCards = cardsWithPermission
.filter((cardWithPermission) => cardWithPermission.hasPermission)
.map((cardWithPermission) => cardWithPermission.card);
// const allowedCards = await this.filterAllowed(userId, cards, Action.read);

return allowedCards;
}

async updateCardHeight(userId: EntityId, cardId: EntityId, height: number): Promise<void> {
this.logger.debug({ action: 'updateCardHeight', userId, cardId, height });

await this.pocCheckPermission(userId, cardId, [Permission.BOARD_CARD_UPDATE]);

const card = await this.cardService.findById(cardId);
await this.checkPermission(userId, card, Action.write);
// await this.checkPermission(userId, card, Action.write);

await this.cardService.updateHeight(card, height);
}

async updateCardTitle(userId: EntityId, cardId: EntityId, title: string): Promise<void> {
this.logger.debug({ action: 'updateCardTitle', userId, cardId, title });

await this.pocCheckPermission(userId, cardId, [Permission.BOARD_CARD_UPDATE]);
const card = await this.cardService.findById(cardId);
await this.checkPermission(userId, card, Action.write);
// await this.checkPermission(userId, card, Action.write);

await this.cardService.updateTitle(card, title);
}

async deleteCard(userId: EntityId, cardId: EntityId): Promise<void> {
this.logger.debug({ action: 'deleteCard', userId, cardId });

await this.pocCheckPermission(userId, cardId, [Permission.BOARD_CARD_DELETE]);
const card = await this.cardService.findById(cardId);
await this.checkPermission(userId, card, Action.write);
// await this.checkPermission(userId, card, Action.write);

await this.cardService.delete(card);
}
Expand All @@ -66,8 +81,10 @@ export class CardUc extends BaseUc {
): Promise<AnyContentElementDo> {
this.logger.debug({ action: 'createElement', userId, cardId, type });

await this.pocCheckPermission(userId, cardId, [Permission.BOARD_ELEMENT_CREATE]);

const card = await this.cardService.findById(cardId);
await this.checkPermission(userId, card, Action.write);
// await this.checkPermission(userId, card, Action.write);

const element = await this.elementService.create(card, type);
if (toPosition !== undefined && typeof toPosition === 'number') {
Expand All @@ -85,11 +102,14 @@ export class CardUc extends BaseUc {
): Promise<void> {
this.logger.debug({ action: 'moveCard', userId, elementId, targetCardId, targetPosition });

await this.pocCheckPermission(userId, elementId, [Permission.BOARD_ELEMENT_MOVE]);
await this.pocCheckPermission(userId, targetCardId, [Permission.BOARD_ELEMENT_MOVE]);

const element = await this.elementService.findById(elementId);
const targetCard = await this.cardService.findById(targetCardId);

await this.checkPermission(userId, element, Action.write);
await this.checkPermission(userId, targetCard, Action.write);
// await this.checkPermission(userId, element, Action.write);
// await this.checkPermission(userId, targetCard, Action.write);

await this.elementService.move(element, targetCard, targetPosition);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export class PermissionContextEntity extends BaseEntityWithTimestamps {
@Property()
userDelta: UserDelta;

@ManyToOne(() => PermissionContextEntity, { wrappedReference: true, fieldName: 'parentContext' })
@ManyToOne(() => PermissionContextEntity, { wrappedReference: true, fieldName: 'parentContext', nullable: true })
@Index()
_parentContext: Reference<PermissionContextEntity> | null;

Expand Down
5 changes: 5 additions & 0 deletions apps/server/src/shared/domain/interface/permission.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,13 @@ export enum Permission {
BOARD_COLUMN_UPDATE_TITLE = 'BOARD_COLUMN_UPDATE_TITLE',

BOARD_CARD_CREATE = 'BOARD_CARD_CREATE',
BOARD_CARD_UPDATE = 'BOARD_CARD_UPDATE',
BOARD_CARD_DELETE = 'BOARD_CARD_DELETE',
BOARD_CARD_MOVE = 'BOARD_CARD_MOVE',

BOARD_ELEMENT_CREATE = 'BOARD_ELEMENT_CREATE',
BOARD_ELEMENT_MOVE = 'BOARD_ELEMENT_MOVE',

/** POC END: BOARD PERMISSIONS */
ACCOUNT_CREATE = 'ACCOUNT_CREATE',
ACCOUNT_EDIT = 'ACCOUNT_EDIT',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ObjectId } from '@mikro-orm/mongodb';
import { Injectable } from '@nestjs/common';
import { EntityId, PermissionContextEntity } from '@shared/domain';
import { EntityId, IPermissionContextProperties, PermissionContextEntity } from '@shared/domain';

Check failure on line 3 in apps/server/src/shared/repo/permission-context/permission-context.repo.ts

View workflow job for this annotation

GitHub Actions / nest_lint

'IPermissionContextProperties' is defined but never used
import { BaseRepo } from '../base.repo';

// TODO: add test
Expand Down

0 comments on commit 464bbc0

Please sign in to comment.