Skip to content

Commit

Permalink
add function to set submission permission
Browse files Browse the repository at this point in the history
  • Loading branch information
EzzatOmar committed Nov 20, 2023
1 parent 464bbc0 commit 8c5a757
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 14 deletions.
2 changes: 2 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 @@ -43,6 +43,8 @@ const DEFAULT_TEACHER_PERMISSIONS = [

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

Expand Down
42 changes: 34 additions & 8 deletions apps/server/src/modules/board/service/content-element.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@ import {
AnyBoardDo,
AnyContentElementDo,
Card,
ColumnBoard,
ContentElementFactory,
ContentElementType,
EntityId,
isAnyContentElement,
Permission,
PermissionContextEntity,
SubmissionItem,
UserDelta,
} from '@shared/domain';
import { PermissionContextRepo } from '@shared/repo';
import { CourseRepo, PermissionContextRepo } from '@shared/repo';
import { ObjectId } from 'bson';
import { AnyElementContentBody } from '../controller/dto';
import { BoardDoRepo } from '../repo';
Expand All @@ -23,7 +26,8 @@ export class ContentElementService {
private readonly boardDoRepo: BoardDoRepo,
private readonly boardDoService: BoardDoService,
private readonly contentElementFactory: ContentElementFactory,
private readonly permissionCtxRepo: PermissionContextRepo
private readonly permissionCtxRepo: PermissionContextRepo,
private readonly courseRepo: CourseRepo
) {}

async findById(elementId: EntityId): Promise<AnyContentElementDo> {
Expand All @@ -45,12 +49,34 @@ export class ContentElementService {
}

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);
if (element instanceof SubmissionItem) {
// NOTE: this will be simplified once we have user groups
const rootId = (await this.boardDoRepo.getAncestorIds(element))[0];
const columnBoard = await this.boardDoRepo.findByClassAndId(ColumnBoard, rootId);
const course = await this.courseRepo.findById(columnBoard.context.id);
const updatedStudentsPermissions = course.students.getItems().map((student) => {
return {
userId: student.id,
includedPermissions: [Permission.BOARD_ELEMENT_CAN_SUBMIT],
excludedPermissions: [],
};
});

const permissionCtxEntity = new PermissionContextEntity({
name: 'Element permission context',
parentContext,
contextReference: new ObjectId(element.id),
userDelta: new UserDelta(updatedStudentsPermissions),
});
await this.permissionCtxRepo.save(permissionCtxEntity);
} else {
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> {
Expand Down
52 changes: 49 additions & 3 deletions apps/server/src/modules/board/service/submission-item.service.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,61 @@
import { ObjectId } from 'bson';
import { Injectable, NotFoundException, UnprocessableEntityException } from '@nestjs/common';

import { EntityId, isSubmissionContainerElement, SubmissionContainerElement, SubmissionItem } from '@shared/domain';
import {
ColumnBoard,
EntityId,
isSubmissionContainerElement,
Permission,
PermissionContextEntity,
SubmissionContainerElement,
SubmissionItem,
UserDelta,
} from '@shared/domain';
import { ValidationError } from '@shared/common';

import { CourseRepo, PermissionContextRepo } from '@shared/repo';
import { BoardDoRepo } from '../repo';
import { BoardDoService } from './board-do.service';

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

private async pocCreateSubmissionItemPermissionCtx(
userId: EntityId,
submissionContainer: SubmissionContainerElement,
submissionItemId: EntityId
) {
// NOTE: this will be simplified once we have user groups
const parentContext = await this.permissionCtxRepo.findByContextReference(submissionContainer.id);

const rootId = (await this.boardDoRepo.getAncestorIds(submissionContainer))[0];
const columnBoard = await this.boardDoRepo.findByClassAndId(ColumnBoard, rootId);
const course = await this.courseRepo.findById(columnBoard.context.id);
const revokeStudentsPermissions = course.students
.getItems()
.filter((student) => student.id !== userId)
.map((student) => {
return {
userId: student.id,
includedPermissions: [],
excludedPermissions: [Permission.BOARD_ELEMENT_CAN_SUBMIT, Permission.BOARD_READ],
};
});

const permissionCtxEntity = new PermissionContextEntity({
name: 'Element permission context',
parentContext,
contextReference: new ObjectId(submissionItemId),
userDelta: new UserDelta(revokeStudentsPermissions),
});
await this.permissionCtxRepo.save(permissionCtxEntity);
}

async findById(id: EntityId): Promise<SubmissionItem> {
const element = await this.boardDoRepo.findById(id);
Expand All @@ -35,7 +81,7 @@ export class SubmissionItemService {
});

submissionContainer.addChild(submissionItem);

await this.pocCreateSubmissionItemPermissionCtx(userId, submissionContainer, submissionItem.id);
await this.boardDoRepo.save(submissionContainer.children, submissionContainer);

return submissionItem;
Expand Down
11 changes: 9 additions & 2 deletions apps/server/src/modules/board/uc/element.uc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
EntityId,
isSubmissionContainerElement,
isSubmissionItem,
Permission,
SubmissionItem,
UserRoleEnum,
} from '@shared/domain';
Expand Down Expand Up @@ -35,14 +36,20 @@ export class ElementUc extends BaseUc {
elementId: EntityId,
content: AnyElementContentBody
): Promise<AnyContentElementDo> {
const element = await this.getElementWithWritePermission(userId, elementId);
await this.pocCheckPermission(userId, elementId, [Permission.BOARD_ELEMENT_UPDATE]);

const element = await this.elementService.findById(elementId);
// const element = await this.getElementWithWritePermission(userId, elementId);

await this.elementService.update(element, content);
return element;
}

async deleteElement(userId: EntityId, elementId: EntityId): Promise<void> {
const element = await this.getElementWithWritePermission(userId, elementId);
await this.pocCheckPermission(userId, elementId, [Permission.BOARD_ELEMENT_DELETE]);

const element = await this.elementService.findById(elementId);
// const element = await this.getElementWithWritePermission(userId, elementId);

await this.elementService.delete(element);
}
Expand Down
20 changes: 20 additions & 0 deletions apps/server/src/shared/domain/entity/permission-context.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,24 @@ export class PermissionContextEntity extends BaseEntityWithTimestamps {

return [...new Set(finalPermissions)];
}

public async resolveFullPermissionMatrix(): Promise<Map<string, Permission[]>> {
const parent = await this.parentContext;
let parentPermissionMatrix: Map<string, Permission[]> = new Map();
if (parent) {
parentPermissionMatrix = await parent.resolveFullPermissionMatrix();
}

const permissionMatrix = new Map(parentPermissionMatrix);

Object.entries(this.userDelta).forEach(([userId, { includedPermissions, excludedPermissions }]) => {
const parentPermissions = parentPermissionMatrix.get(userId) ?? [];
const permissions = includedPermissions
.concat(parentPermissions)
.filter((permission) => !excludedPermissions.includes(permission));
permissionMatrix.set(userId, permissions);
});

return permissionMatrix;
}
}
3 changes: 3 additions & 0 deletions apps/server/src/shared/domain/interface/permission.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ export enum Permission {
BOARD_CARD_MOVE = 'BOARD_CARD_MOVE',

BOARD_ELEMENT_CREATE = 'BOARD_ELEMENT_CREATE',
BOARD_ELEMENT_UPDATE = 'BOARD_ELEMENT_UPDATE',
BOARD_ELEMENT_DELETE = 'BOARD_ELEMENT_DELETE',
BOARD_ELEMENT_MOVE = 'BOARD_ELEMENT_MOVE',
BOARD_ELEMENT_CAN_SUBMIT = 'BOARD_SUBMISSION_CAN_SUBMIT',

/** POC END: BOARD PERMISSIONS */
ACCOUNT_CREATE = 'ACCOUNT_CREATE',
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, IPermissionContextProperties, PermissionContextEntity } from '@shared/domain';
import { EntityId, PermissionContextEntity } from '@shared/domain';
import { BaseRepo } from '../base.repo';

// TODO: add test
Expand Down

0 comments on commit 8c5a757

Please sign in to comment.