diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..b58b603fea78041071d125a30db58d79b3d49217 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/dojobackendapi.iml b/.idea/dojobackendapi.iml new file mode 100644 index 0000000000000000000000000000000000000000..24643cc37449b4bde54411a80b8ed61258225e34 --- /dev/null +++ b/.idea/dojobackendapi.iml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="WEB_MODULE" version="4"> + <component name="NewModuleRootManager"> + <content url="file://$MODULE_DIR$"> + <excludeFolder url="file://$MODULE_DIR$/.tmp" /> + <excludeFolder url="file://$MODULE_DIR$/temp" /> + <excludeFolder url="file://$MODULE_DIR$/tmp" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + </component> +</module> \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000000000000000000000000000000000000..53aaca2013eb5b498566e71713e4c4feca197ee3 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectModuleManager"> + <modules> + <module fileurl="file://$PROJECT_DIR$/.idea/dojobackendapi.iml" filepath="$PROJECT_DIR$/.idea/dojobackendapi.iml" /> + </modules> + </component> +</project> \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000000000000000000000000000000000000..35eb1ddfbbc029bcab630581847471d7f238ec53 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="VcsDirectoryMappings"> + <mapping directory="" vcs="Git" /> + </component> +</project> \ No newline at end of file diff --git a/ExpressAPI/.env.vault b/ExpressAPI/.env.vault index 22cc6dca141b734cda92ccf8dd7563c76c93a6a2..9ef7138fd136d8b0846f7b733085911a1f2b9696 100644 --- a/ExpressAPI/.env.vault +++ b/ExpressAPI/.env.vault @@ -4,11 +4,11 @@ #/--------------------------------------------------/ # development -DOTENV_VAULT_DEVELOPMENT="m/zyf+mqjCxtkvbnip0/TT5WzskG7kilIGtGtjBhPYeWDYjX1fIgUgiLANjNDKcawWoNcwddl6vHuYwHrqi89uP+DiIonM0Oycxt1pQI6Yjtmtzbx0bwcs/7dTjr3ORna/1b7wRNPyIdPoGz1/B15R0p6RiotXKz91egMmNAN1d13gqou9JAQfbUcGnVupnMoRVt0GoKpHN54uF0zy1aW0sEp4NcHhU0KRu3B3HKZGKrpCMoi35S26GZEA1DNwKaIaNJKnwQCviRiV0jIyQXBFpbEUGbYIi3Rr2C4oM2/GW0JTsiNjbrHf7ZYCKOrXAXrBsE0Evmhc/zgUlsBxBpB7zFspgULgibEk18SPaNAibMT8o0ZJeoYIyL6OVo7Cqel4/FP9I/1DBX8NGNtmPwYKQQYngFh2OtIfVjBBwoiQkSwQiT4L8yoBz1S/rkhini2BVa1ibruhLTLDD9uiOEub63F4J0SaXgbPin3wz0hr4Jfn3Z8cEbXmD/fh1KjI3nIef6D7W7rUPCZn6OxISYnMKZo97asYF1NGuzoGxSmqMH34EsiTDKFbcPC96rTnywbGf+eHry5lE7z4r6HeFpPKpasvqOAtoGauuXBJnQDBKio6dz/jB+LlVoykcblBY3oUACq0wdBcdX6S3b2NtiX7cG2YrqXtTAeeMI/7QQ/P10R0QQe2tO+3Zf2lPKCUN4D5XYFA89YpIUQ+jR8nWdk0lPHOJFovzk/THt5latUpIeDy2Zj7lPGwpPKIUuJ1/1zOLAr5AYGePZz24eB0Ge30ElyKBIc5PnWXBVwm+zT4bOuJliOiY20gMhYcOE96Fp8kFmat4lZBo+caAEU9x/tPyqWmusd1LgGfUB5LXuYRPbVoN3S3smKqEkHNQ9l8sdwg3jlQUqzjQrxazn4ONGkRPqdwdw1sBSiMumePhLFt7swUIYiw2B0SbP2Lef8dYuHFTu64464pT1JY1uR4dOqr6Cnwa/eZpu1m1TuMQhI4pmuux0735VNnslHmTeXty0kNuPQ58TU5n+l9kptzC/GYvx2eYEFh3CYCG3Dh6twDGQf+bDaV+aq7R1Eo1VUscS60X30n5KpdQQsFA95Xn2GMbGQdgVdFFclj5gbx+Up/ikqDyCSPxqRtc/4eTa1SH4N09EQgtVANpUPJwddglXEMjv4v+KDd4waGxnY+J5HCGIe9t+Q+eZgrqYv3GLddBoRKHa31oZEwSIVblFG3drbkZrLDGevVssEngHS3q4Ki6y10wyHlWnQQ4UEc6b8nvJTjfqizxdnzfo3q5yDslo65VyEhlBkOlQhGSPh4hfyawnh9bSud2YsMlMDuFg4iQZ4IxEkMEC7P2QlVSN1cHXYcAlMpH9L5hChRyxK7EzBB2lgFh4w5NU0X0Asd7AiVixOl6W+9Lq1cobfwD+/Lud03xjjYdJwVH2sLLF2FyW/dNxvcA+kEKE/PcS4vrtKz/PknozDMzreVKcHs4LRG4Q5XPKz1uPIKl2MYoui+unmjLrJ66zv7yA795w1IhlgAa7hrU9hhie4O2c9jGmB2dIXnJ5D8DSm1n3c3lZjCquAi1/u7ieRHsybsEOxWe/AaxV/9vPq2B8I4Sy6deWmPxTtbcrFnXWZo1j1dzr0HZ1l4jdiwFFSIN9/HmEJVlUbK97rcKP7KQubmrQ4xq4BcNqB+R8eg1ip+Q6RMt/L3Qt29ZaRSWZ1RDRtylPZMvFJVzQ+L/T96ueCFg3f55V/woSp1y2iBj1QXEpLHmNvtj3YyXs0I4JDPlrqs0lNDKhS7ysYDJDVuizs1IgCD6qZ6h9I5+heSsBv7/A3zB3cXIdrO4uo8GfObHBYFDbmhDfNoofxgLGSMT2M1V/zmViseRXh4LvbolhU6Ny5J91kOafbrpMa8UY03OsHkdp5vW8+iW40U9p/UV8svFRlUNQqdn4l5cVgpyu5IgUbWWfgeCWZcyh2pWSHQ7pR0NUuHxMiLjUnge/LlE3n0tMb85Z99ql8o0bKV3cw1HsrO3rWig0sSGTrvqc0XLCjKeKFPD8wlZENS6T+fT3fmr2PVtcYKy8gD7QWiDwY0Ig1HZnXd8cpWwKYm+c7tFnmvvDZNAmsxqu/Vin1yUDBjA6pH95la6OsAhXK0IPxDTirUd7uAH0AC0aNKrGsDIKgdSwQrHqGsQKCPgsrTad53N3gbmmkX3UWgNVwaTOq2gKgQTSVeJkqUWPwbnsvqwiZlrvLmdTh1qA4wgUVDqZd/O77m34YNmL7njEUM6ttaoj89fLJSxRzEox+otXol9LwKkB0q+0FxYdNJOxVtdsURjVd9egauoDXqwW5p6tIYx6c7eC7tOejpTbJl4254vNjElTpqDTJT8tXYjuYvXkpFzTET0rhuosKdH8yBEftZ5/y3k1i7ls5s3XDk1PCy5dWRGl92VcLLaE3XTxYK+1FxApsZzvmfWEE0UdI2rp+36IoMwRCs4kAAKTFu/5JSUOsHGoofYvrvIjTzrzXRaMyKdD1geA9cuO8JWJAXnVS/RANNtvco5tGv760GUM/ZfVSCNCxV7WQRCNpSv6j7HnLuQrDrUv8PGBBGLGrH8ak9F9z+p2N8BsTTAQF6pbsq9dycWZ1u0a/oobM8js3SiEggcL1bfkfsTby2hvwrhUDa/QvXAKhr6c46i/qCiJK0BIaVhk7GA9ByPdDao+bzDROMg8Tr/fI1zmTN7rpUQAc8u2vIbHaWUTQ09xWNGDEymRpx/VG6HAHZQEffioXCiOMz5QGMIHu9CIQvcQ2dqwty/Rtu8NpP7HVli78leA5B6UeePwYCZ1btTP4rpfOYaU4CaSpUuEpVaLwV7WzFzlpbiIeTuvqObFys6YkBsP4iiVlUjPrIE4RD1CE84cipIl91TNFVbx5JE5NdZBD8zJ645q9BpqPNd/cSxGRd9yJoUkvDvJ/X+izZw9YtwzDZPUUg0B0BE1M8D8OlkYI1LLsOEz92HX6+1HnZSABafxfw/xrP0A1ZoKDHx46cCe/SH0B4PZKFo4rDK8S6bA5Vf8m7NOnbPoW805rUFVl6erB0rsH+7boYHVhUFOgTO4wIG5uwYHVvKdbyPk+peeBKBcTkaCsPeaLUyeMW9WG3TKi5c245bWsVIz493IRS8jSMzyY/kfdUJRVeFL6It/MOVSMuNd9Qzm8HyVNmwOhb8D/u67RGdKTmrbx1K/8EtSIiMUbUvjU2o0yk3PLQQ/2+wKWE5WErq0yybvqtVy2tm5aqDhL6XdjtzcriluhpuSFnM24kBIuOllzuT/eq1ntB2Yn3rfihtDqQjiiBO/IHL0tiJuHQatyMzjIF2flbHVQlDwDcPcb3GOu4XSpo29gezjHkizAL5H7771ud/j191L/758Rm1dVAAqquu1cGIzr5cyH71p85M6DeqAL1pEDZg4jdOCzNYtdbG7uaRC78dKrhC+QBUvBMqPNS5qgV5ghssJ2mm4cC8hS7kRPu1TMJkqetSkJ0B66MSbvylfDkaEuHh7RyBr9G99cwI48EE79MCxKWPANMzxljcl3XG6Xi0stQ0wa3z3sPf+L+Ya0nM7so9TKxi/9oXE2B4aXcA+dH4hyFaqLGtX6t0bJu4QaTVvEeMAlKJecY0qeNms+Wv2+i1pFHWzwQmgSKfUcfUfze0BKD4Bv0C2NHNP/kZqEQsAmXWmC22xphIqvThamGVnfDR33ggankhcOSk4VI+JIsLIF7xXlphuN4J9f0peVNeXCS5Isvc+9uPeYftwrj5JyEXbDjGKfgDS25X+QKyvWBXBzIQ=" +DOTENV_VAULT_DEVELOPMENT="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" # production -DOTENV_VAULT_PRODUCTION="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" +DOTENV_VAULT_PRODUCTION="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" # test -DOTENV_VAULT_TEST="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" +DOTENV_VAULT_TEST="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" diff --git a/ExpressAPI/prisma/migrations/20240523212938_add_deleted_to_exercise/migration.sql b/ExpressAPI/prisma/migrations/20240523212938_add_deleted_to_exercise/migration.sql new file mode 100644 index 0000000000000000000000000000000000000000..4d82e3a5c8054818e93ff3fdece07ad890f4cb64 --- /dev/null +++ b/ExpressAPI/prisma/migrations/20240523212938_add_deleted_to_exercise/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE `Exercise` ADD COLUMN `deleted` BOOLEAN NOT NULL DEFAULT false; diff --git a/ExpressAPI/prisma/schema.prisma b/ExpressAPI/prisma/schema.prisma index 69e3b2678b3e6e6548bc86c6f713b5560ccc31f0..b9fc889ad8b8374cc56d80d43e4e775a8f776e77 100644 --- a/ExpressAPI/prisma/schema.prisma +++ b/ExpressAPI/prisma/schema.prisma @@ -57,6 +57,7 @@ model Exercise { gitlabCreationInfo Json @db.Json gitlabLastInfo Json @db.Json gitlabLastInfoDate DateTime + deleted Boolean @default(false) correctionCommit Json? @db.Json correctionDescription String? @db.VarChar(80) diff --git a/ExpressAPI/src/config/Config.ts b/ExpressAPI/src/config/Config.ts index 9d5766cae888d80ac7d705cece888ea8124170d4..29301c0cc30fca094b4d1b3b7ba59de1c4c99085 100644 --- a/ExpressAPI/src/config/Config.ts +++ b/ExpressAPI/src/config/Config.ts @@ -44,7 +44,7 @@ class Config { }; account: { id: number; username: string; token: string; }; group: { - root: number; templates: number; assignments: number; exercises: number; + root: number; templates: number; assignments: number; exercises: number; deletedAssignments: number; deletedExercises: number; }, badges: { pipeline: ConfigGitlabBadge } @@ -98,10 +98,12 @@ class Config { timeoutAfterCreation: Number(process.env.GITLAB_REPOSITORY_CREATION_TIMEOUT || 5000) }, group : { - root : Number(process.env.GITLAB_GROUP_ROOT_ID || 0), - templates : Number(process.env.GITLAB_GROUP_TEMPLATES_ID || 0), - assignments: Number(process.env.GITLAB_GROUP_ASSIGNMENTS_ID || 0), - exercises : Number(process.env.GITLAB_GROUP_EXERCISES_ID || 0) + root : Number(process.env.GITLAB_GROUP_ROOT_ID || 0), + templates : Number(process.env.GITLAB_GROUP_TEMPLATES_ID || 0), + assignments : Number(process.env.GITLAB_GROUP_ASSIGNMENTS_ID || 0), + exercises : Number(process.env.GITLAB_GROUP_EXERCISES_ID || 0), + deletedAssignments: Number(process.env.GITLAB_GROUP_DELETED_ASSIGNMENTS_ID || 0), + deletedExercises : Number(process.env.GITLAB_GROUP_DELETED_EXERCISES_ID || 0) }, badges : { pipeline: { diff --git a/ExpressAPI/src/managers/ExerciseManager.ts b/ExpressAPI/src/managers/ExerciseManager.ts index 258f96379bfe4528e8b59fed8f7be3e78097aeea..bc64be63d2f0e3ec5970ea1400113d421aed8278 100644 --- a/ExpressAPI/src/managers/ExerciseManager.ts +++ b/ExpressAPI/src/managers/ExerciseManager.ts @@ -1,6 +1,6 @@ -import { Prisma } from '@prisma/client'; -import { Exercise } from '../types/DatabaseTypes.js'; -import db from '../helpers/DatabaseHelper.js'; +import { Prisma } from '@prisma/client'; +import { Exercise, User } from '../types/DatabaseTypes.js'; +import db from '../helpers/DatabaseHelper.js'; class ExerciseManager { @@ -23,6 +23,24 @@ class ExerciseManager { include: include }) as Promise<Array<Exercise>>; } + + async isUserAllowedToAccessExercise(exercise: Exercise, user: User): Promise<boolean> { + if ( !exercise.members ) { + exercise.members = await db.exercise.findUnique({ + where: { + id: exercise.id + } + }).members() ?? []; + } + + const assignmentStaff = (await db.assignment.findUnique({ + where: { + name: exercise.assignmentName + } + }).staff()) ?? []; + + return exercise.members.findIndex(member => member.id === user.id) !== -1 || assignmentStaff.findIndex(staff => staff.id === user.id) !== -1; + } } diff --git a/ExpressAPI/src/managers/GitlabManager.ts b/ExpressAPI/src/managers/GitlabManager.ts index bd2746ab4323f39d16b3ab7cee410c463b42ddfe..5ad9dca9dd1bad96492bdb4dd32040c040442aaa 100644 --- a/ExpressAPI/src/managers/GitlabManager.ts +++ b/ExpressAPI/src/managers/GitlabManager.ts @@ -29,9 +29,9 @@ class GitlabManager extends SharedGitlabManager { } } - async getRepositoryMembers(idOrNamespace: string): Promise<Array<MemberSchema>> { + async getRepositoryMembers(idOrNamespace: string, includeInherited: boolean = true): Promise<Array<MemberSchema>> { try { - return await this.api.ProjectMembers.all(idOrNamespace, { includeInherited: true }); + return await this.api.ProjectMembers.all(idOrNamespace, { includeInherited: includeInherited }); } catch ( e ) { logger.error(JSON.stringify(e)); return Promise.reject(e); @@ -122,6 +122,27 @@ class GitlabManager extends SharedGitlabManager { } } + async deleteRepositoryMember(repoId: number, userId: number, skipSubresources: boolean = false, unassignIssuables: boolean = false): Promise<void> { + try { + return await this.api.ProjectMembers.remove(repoId, userId, { + skipSubresourceS: skipSubresources, + unassignIssuables + }); + } catch ( e ) { + logger.error(JSON.stringify(e)); + return Promise.reject(e); + } + } + + async moveRepository(repoId: number, newRepoId: number): Promise<ProjectSchema> { + try { + return await this.api.Projects.transfer(repoId, newRepoId); + } catch ( e ) { + logger.error(JSON.stringify(e)); + return Promise.reject(e); + } + } + changeRepositoryVisibility(repoId: number, visibility: GitlabVisibility): Promise<ProjectSchema> { return this.editRepository(repoId, { visibility: visibility }); } diff --git a/ExpressAPI/src/middlewares/SecurityMiddleware.ts b/ExpressAPI/src/middlewares/SecurityMiddleware.ts index 6edc8bee8f308084bdf03e542f0f91949578788e..bfe307ecf2adf5b1cbc774565add672d5022df09 100644 --- a/ExpressAPI/src/middlewares/SecurityMiddleware.ts +++ b/ExpressAPI/src/middlewares/SecurityMiddleware.ts @@ -3,6 +3,7 @@ import { StatusCodes } from 'http-status-codes'; import SecurityCheckType from '../types/SecurityCheckType.js'; import logger from '../shared/logging/WinstonLogger.js'; import AssignmentManager from '../managers/AssignmentManager.js'; +import ExerciseManager from '../managers/ExerciseManager'; class SecurityMiddleware { @@ -17,6 +18,8 @@ class SecurityMiddleware { return req.session.profile.isAdmin; case SecurityCheckType.TEACHING_STAFF.valueOf(): return req.session.profile.isTeachingStaff; + case SecurityCheckType.EXERCISE_MEMBERS.valueOf(): + return await ExerciseManager.isUserAllowedToAccessExercise(req.boundParams.exercise!, req.session.profile); case SecurityCheckType.ASSIGNMENT_STAFF.valueOf(): return await AssignmentManager.isUserAllowedToAccessAssignment(req.boundParams.assignment!, req.session.profile); case SecurityCheckType.ASSIGNMENT_IS_PUBLISHED.valueOf(): diff --git a/ExpressAPI/src/routes/ExerciseRoutes.ts b/ExpressAPI/src/routes/ExerciseRoutes.ts index b32ab8b22983feed2cef9d06768bedfc0b70b2c6..463f353a4e47c9cb2ace3b91be3f3fad2018d3ab 100644 --- a/ExpressAPI/src/routes/ExerciseRoutes.ts +++ b/ExpressAPI/src/routes/ExerciseRoutes.ts @@ -67,7 +67,12 @@ class ExerciseRoutes implements RoutesManager { registerOnBackend(backend: Express) { backend.post('/assignments/:assignmentNameOrUrl/exercises', SecurityMiddleware.check(true, SecurityCheckType.ASSIGNMENT_IS_PUBLISHED), ParamsValidatorMiddleware.validate(this.exerciseValidator), this.createExercise.bind(this) as RequestHandler); + backend.get('/exercises', SecurityMiddleware.check(true, SecurityCheckType.ADMIN), this.getAllExercises.bind(this) as RequestHandler); backend.get('/exercises/:exerciseIdOrUrl/assignment', SecurityMiddleware.check(false, SecurityCheckType.EXERCISE_SECRET), this.getAssignment.bind(this) as RequestHandler); + backend.get('/exercises/:exerciseIdOrUrl/members', SecurityMiddleware.check(true, SecurityCheckType.ADMIN, SecurityCheckType.EXERCISE_MEMBERS), this.getExerciseMembers.bind(this) as RequestHandler); + backend.get('/exercises/:exerciseIdOrUrl/results', SecurityMiddleware.check(true, SecurityCheckType.ADMIN, SecurityCheckType.EXERCISE_MEMBERS), this.getExerciseResults.bind(this) as RequestHandler); + + backend.delete('/exercises/:exerciseIdOrUrl', SecurityMiddleware.check(true, SecurityCheckType.ADMIN, SecurityCheckType.EXERCISE_MEMBERS), this.deleteExercise.bind(this) as RequestHandler); backend.post('/exercises/:exerciseIdOrUrl/results', SecurityMiddleware.check(false, SecurityCheckType.EXERCISE_SECRET), ParamsValidatorMiddleware.validate(this.resultValidator), this.createResult.bind(this) as RequestHandler); } @@ -78,10 +83,55 @@ class ExerciseRoutes implements RoutesManager { return `DojoEx - ${ assignment.name } - ${ memberNames }${ suffixString }`; } + private async getExerciseMembers(req: express.Request, res: express.Response) { + const repoId = req.boundParams.exercise!.gitlabId; + + const members = await GitlabManager.getRepositoryMembers(String(repoId)); + + return req.session.sendResponse(res, StatusCodes.OK, members); + } + + private async getExerciseResults(req: express.Request, res: express.Response) { + const results = await db.result.findMany({ + where : { exerciseId: req.boundParams.exercise!.id }, + orderBy: { dateTime: 'desc' } + }); + + return req.session.sendResponse(res, StatusCodes.OK, results); + } + + private async deleteExercise(req: express.Request, res: express.Response) { + const repoId = req.boundParams.exercise!.gitlabId; + + const members = await GitlabManager.getRepositoryMembers(String(repoId), false); + for ( const member of members ) { + if ( member.id !== Config.gitlab.account.id ) { + await GitlabManager.deleteRepositoryMember(repoId, member.id); + } + } + + await GitlabManager.moveRepository(repoId, Config.gitlab.group.deletedExercises); + + await db.exercise.update({ + where: { id: req.boundParams.exercise!.id }, + data : { deleted: true } + }); + + return req.session.sendResponse(res, StatusCodes.OK); + } + private getExercisePath(assignment: Assignment, exerciseId: string): string { return `dojo-ex_${ (assignment.gitlabLastInfo as unknown as Gitlab.ProjectSchema).path }_${ exerciseId }`; } + // Get all exercise + private async getAllExercises(req: express.Request, res: express.Response) { + const exos = await db.exercise.findMany(); + + return req.session.sendResponse(res, StatusCodes.OK, exos); + } + + private async checkExerciseLimit(assignment: Assignment, members: Array<Gitlab.UserSchema>): Promise<Array<Gitlab.UserSchema>> { const exercises: Array<Exercise> | undefined = await ExerciseManager.getFromAssignment(assignment.name, { members: true }); const reachedLimitUsers: Array<Gitlab.UserSchema> = []; @@ -259,3 +309,5 @@ class ExerciseRoutes implements RoutesManager { export default new ExerciseRoutes(); + + diff --git a/ExpressAPI/src/types/SecurityCheckType.ts b/ExpressAPI/src/types/SecurityCheckType.ts index 17d026a253327907355176e847a6ac4a7047922f..e6f122b576e49776c4c398a124ae7fcabc9c6367 100644 --- a/ExpressAPI/src/types/SecurityCheckType.ts +++ b/ExpressAPI/src/types/SecurityCheckType.ts @@ -1,6 +1,7 @@ enum SecurityCheckType { TEACHING_STAFF = 'teachingStaff', ADMIN = 'admin', + EXERCISE_MEMBERS = 'exerciseMembers', ASSIGNMENT_STAFF = 'assignmentStaff', ASSIGNMENT_IS_PUBLISHED = 'assignmentIsPublished', EXERCISE_SECRET = 'exerciseSecret',