Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ A user can have one of the following roles:
- Can CRUD user- and group-permissions for managed users and administrated groups.
- `STUDENT`: The user has limited access to the system and can only manage their own resources.


\* Documents can be updated always only by the user who created them. Except the document has excplicite shared permissions with other users/groups.

## Code Formatting
Expand Down
22 changes: 20 additions & 2 deletions src/controllers/documentRoots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { HTTP400Error, HTTP403Error } from '../utils/errors/Errors.js';
import Document from '../models/Document.js';
import { NoneAccess, RO_RW_DocumentRootAccess } from '../helpers/accessPolicy.js';
import { hasElevatedAccess } from '../models/User.js';
import { Access } from '../../prisma/generated/enums.js';

export const find: RequestHandler<{ id: string }> = async (req, res, next) => {
const document = await DocumentRoot.findModel((req as any).user!, req.params.id);
Expand Down Expand Up @@ -151,10 +152,27 @@ export const update: RequestHandler<{ id: string }, any, UpdateConfig> = async (
res.status(204).send();
};

export const permissions: RequestHandler<{ id: string }> = async (req, res, next) => {
const permissions = await DocumentRoot.getPermissions((req as any).user!, req.params.id);
export const permissions: RequestHandler<any, any, { documentRootIds: string[] }> = async (
req,
res,
next
) => {
const permissions = await DocumentRoot.getPermissions((req as any).user!, req.body.documentRootIds);
res.json(permissions);
};
// TODO: remove this endpoint once the permissions [POST]/documentRoots/permissions endpoint is established and clients are updated
export const singlePermissions: RequestHandler<{ id: string }> = async (req, res, next) => {
const permissions = await DocumentRoot.getPermissions((req as any).user!, [req.params.id]);
res.json(
permissions[0] ?? {
id: req.params.id,
access: Access.None_DocumentRoot,
sharedAccess: Access.None_DocumentRoot,
userPermissions: [],
groupPermissions: []
}
);
};

export const destroy: RequestHandler<{ id: string }> = async (req, res, next) => {
const model = await DocumentRoot.deleteModel((req as any).user!, req.params.id);
Expand Down
52 changes: 30 additions & 22 deletions src/models/DocumentRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export type ApiDocumentRoot = DbDocumentRoot & {

type Permissions = {
id: string;
access: Access;
sharedAccess: Access;
userPermissions: ApiUserPermission[];
groupPermissions: ApiGroupPermission[];
};
Expand Down Expand Up @@ -50,11 +52,13 @@ export interface UpdateConfig {
sharedAccess?: Access;
}

const prepareGroupPermission = (permission: RootGroupPermission): ApiGroupPermission => {
const prepareGroupPermission = (
permission: Omit<RootGroupPermission, 'documentRootId'>
): ApiGroupPermission => {
return { id: permission.id, access: permission.access, groupId: permission.studentGroupId };
};

const prepareUserPermission = (permission: RootUserPermission): ApiUserPermission => {
const prepareUserPermission = (permission: Omit<RootUserPermission, 'documentRootId'>): ApiUserPermission => {
return { id: permission.id, access: permission.access, userId: permission.userId };
};

Expand Down Expand Up @@ -207,30 +211,34 @@ function DocumentRoot(db: PrismaClient['documentRoot']) {
groupPermissions: model.rootGroupPermissions.map((p) => prepareGroupPermission(p))
};
},
async getPermissions(actor: User, id: string): Promise<Permissions> {
async getPermissions(actor: User, ids: string[]): Promise<Permissions[]> {
if (!hasElevatedAccess(actor.role)) {
throw new HTTP403Error('Not authorized');
}
const userPermissions = await prisma.rootUserPermission.findMany({
where:
actor.role === Role.ADMIN
? { documentRootId: id }
: { documentRootId: id, user: whereStudentGroupAccess(actor.id, true) }
});
const groupPermissions = await prisma.rootGroupPermission.findMany({
where:
actor.role === Role.ADMIN
? { documentRootId: id }
: {
documentRootId: id,
studentGroup: { users: { some: { userId: actor.id, isAdmin: true } } }
}
const permissions = await db.findMany({
where: { id: { in: ids } },
include: {
rootUserPermissions: {
select: { id: true, access: true, userId: true },
where:
actor.role === Role.ADMIN ? {} : { user: whereStudentGroupAccess(actor.id, true) }
},
rootGroupPermissions: {
select: { id: true, access: true, studentGroupId: true },
where:
actor.role === Role.ADMIN
? {}
: { studentGroup: { users: { some: { userId: actor.id, isAdmin: true } } } }
}
}
});
return {
id: id,
userPermissions: userPermissions.map(prepareUserPermission),
groupPermissions: groupPermissions.map(prepareGroupPermission)
};
return permissions.map((p) => ({
id: p.id,
access: p.access,
sharedAccess: p.sharedAccess,
userPermissions: p.rootUserPermissions.map(prepareUserPermission),
groupPermissions: p.rootGroupPermissions.map(prepareGroupPermission)
}));
},
async deleteModel(actor: User, id: string) {
const record = await this.findModel(actor, id);
Expand Down
5 changes: 5 additions & 0 deletions src/routes/authConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ const authConfig: Config = {
{ methods: ['PUT', 'DELETE'], minRole: Role.TEACHER }
]
},
documentRootMultiPermissions: {
path: '/documentRoots/permissions',
access: [{ methods: ['POST'], minRole: Role.TEACHER }]
},
// TODO: remove this endpoint once the permissions [POST]/documentRoots/permissions endpoint is established and clients are updated
documentRootPermissions: {
path: '/documentRoots/:id/permissions',
access: [{ methods: ['GET'], minRole: Role.TEACHER }]
Expand Down
11 changes: 5 additions & 6 deletions src/routes/event-handlers/joinRoom.handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,24 @@ import { ClientToServerEvents, IoClientEvent, ServerToClientEvents } from '../so
import type { DefaultEventsMap, Socket } from 'socket.io';
import prisma from '../../prisma.js';
import StudentGroup from '../../models/StudentGroup.js';
import onStreamUpdate, { onStreamDynamicRoomUpdate } from './streamUpdate.handler.js';
import { onStreamDynamicRoomUpdate } from './streamUpdate.handler.js';
import DocumentRoot from '../../models/DocumentRoot.js';
import { highestAccess, RWAccess } from '../../helpers/accessPolicy.js';
import { Role } from '../../models/User.js';
import Logger from '../../utils/logger.js';
type SocketType = Socket<ClientToServerEvents, ServerToClientEvents, DefaultEventsMap, any>;

const isDocumentRoot = (roomId: string) => {
return prisma.documentRoot.findFirst({ where: { id: roomId } });
};

const findDocumentRoot = (user: User, roomId: string) => {
return DocumentRoot.getPermissions(user, roomId).then((res) => {
if (!res) {
return DocumentRoot.getPermissions(user, [roomId]).then((res) => {
if (!res || res.length !== 1) {
return false;
} else {
const access = new Set([
...res.groupPermissions.map((p) => p.access),
...res.userPermissions.map((p) => p.access)
...res[0].groupPermissions.map((p) => p.access),
...res[0].userPermissions.map((p) => p.access)
]);
const current = highestAccess(access);
return RWAccess.has(current);
Expand Down
8 changes: 6 additions & 2 deletions src/routes/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
findMany as findManyDocumentRoots,
update as updateDocumentRoot,
permissions as allPermissions,
singlePermissions as allPermissionsFor,
findManyFor as findManyDocumentRootsFor,
findMultipleFor as findMultipleDocumentRootsFor,
allDocuments,
Expand Down Expand Up @@ -99,12 +100,15 @@ router.delete('/permissions/group/:id', deleteStudentGroupPermission);

router.get('/documentRoots', findManyDocumentRoots);
router.get('/documentRoots/:id', findDocumentRoot);
// TODO: remove this endpoint once the permissions [POST]/documentRoots/permissions endpoint is established and clients are updated
router.get('/documentRoots/:id/permissions', allPermissionsFor);
// order matters here! /documentRoots/:id would match /documentRoots/:id/permissions if it was placed before
router.post('/documentRoots/permissions', allPermissions);
router.post('/documentRoots/:id', createDocumentRoot);
router.put('/documentRoots/:id', updateDocumentRoot);
router.delete('/documentRoots/:id', deleteDocumentRoot);
router.get('/documentRoots/:id/permissions', allPermissions);
router.post('/documents', createDocument);

router.post('/documents', createDocument);
/**
* TODO: remove once /documents/multiple is established and clients are updated
*
Expand Down
Loading