Skip to content

Commit

Permalink
feat(core): add course submission mode
Browse files Browse the repository at this point in the history
  • Loading branch information
Zitrone44 committed Nov 18, 2024
1 parent 4130640 commit 1b041e5
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 14 deletions.
3 changes: 3 additions & 0 deletions modules/fbs-core/api/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ services:
ids:
salt: ${ID_SALT:feedbacksystem_id_salt}
length: ${ID_LENGTH:8}
ip:
vpnCidrs: ${IP_VPN_CIDR:""}
localCidrs: ${IP_LOCAL_CIDR:""}
spring:
main:
allow-bean-definition-overriding: true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
BEGIN;

ALTER TABLE course ADD COLUMN submission_mode ENUM('internet', 'vpn', 'local') NOT NULL DEFAULT 'internet';

INSERT INTO migration (number) VALUES (23);

COMMIT;

Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,13 @@ class CourseController {
body.retrive("semesterId").asInt(),
body.retrive("name").asText(),
body.retrive("description").asText(),
body.retrive("visible").asBool()
body.retrive("visible").asBool(),
body.retrive("submissionMode").asText(),
) match {
case (semesterId, Some(name), desc, visible) =>
courseService.create(Course(name, desc.getOrElse(""), visible.getOrElse(true), semesterId = semesterId))
case (semesterId, Some(name), desc, visible, submissionMode) =>
courseService.create(
Course(name, desc.getOrElse(""), visible.getOrElse(true), semesterId = semesterId, submissionMode = submissionMode.getOrElse("internet"))
)
case _ => throw new BadRequestException("Malformed Request Body")
}
}
Expand Down Expand Up @@ -128,10 +131,14 @@ class CourseController {
body.retrive("semesterId").asInt(),
body.retrive("name").asText(),
body.retrive("description").asText(),
body.retrive("visible").asBool()
body.retrive("visible").asBool(),
body.retrive("submissionMode").asText(),
) match {
case (semesterId, Some(name), desc, visible) =>
courseService.update(cid, Course(name, desc.getOrElse(""), visible.getOrElse(true), semesterId = semesterId))
case (semesterId, Some(name), desc, visible, submissionMode) =>
courseService.update(
cid,
Course(name, desc.getOrElse(""), visible.getOrElse(true), semesterId = semesterId, submissionMode = submissionMode.getOrElse("internet"))
)
case _ => throw new BadRequestException("Malformed Request Body")
}
case _ => throw new ForbiddenException()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import de.thm.ii.fbs.model.{CourseRole, GlobalRole, Submission}
import de.thm.ii.fbs.services.checker.CheckerServiceFactoryService
import de.thm.ii.fbs.services.persistence._
import de.thm.ii.fbs.services.persistence.storage.{MinioStorageService, StorageService}
import de.thm.ii.fbs.services.security.AuthService
import de.thm.ii.fbs.services.security.{AuthService, IpService}
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.core.io.InputStreamResource
Expand Down Expand Up @@ -52,7 +52,11 @@ class SubmissionController {
@Autowired
private val courseService: CourseService = null
@Autowired
private val ipService: IpService = null
@Autowired
private val courseRegistration: CourseRegistrationService = null
@Autowired
private val request: HttpServletRequest = null
private val objectMapper = new ObjectMapper();
private val logger = LoggerFactory.getLogger(this.getClass)

Expand Down Expand Up @@ -137,6 +141,14 @@ class SubmissionController {
def submit(@PathVariable("uid") uid: Int, @PathVariable("cid") cid: Int, @PathVariable("tid") tid: Int,
@RequestParam file: MultipartFile, @RequestParam additionalInformation: Optional[String],
req: HttpServletRequest, res: HttpServletResponse): Submission = {
val course = courseService.find(cid)
course match {
case Some(course) =>
if (!ipService.isIpInZone(course.submissionMode, Option(request.getHeader("X-Real-IP")).getOrElse(request.getRemoteAddr))) {
throw new ForbiddenException()
}
case None => throw new ResourceNotFoundException()
}
val user = authService.authorize(req, res)
val someCourseRole = courseRegistration.getCourseRoleOfUser(cid, user.id)
val noPrivateAccess = someCourseRole.contains(CourseRole.STUDENT) && user.globalRole != GlobalRole.ADMIN
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ package de.thm.ii.fbs.model
* @param groupSelection Whether registration for groups is possible
*/
case class Course(name: String, description: String = "", visible: Boolean = true, id: Int = 0, semesterId: Option[Int] = None,
groupSelection: Option[Boolean] = None)
groupSelection: Option[Boolean] = None, submissionMode: String = "internet")
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ class CourseService {
* @return List of courses
*/
def findByPattern(pattern: String, ignoreHidden: Boolean = true): List[Course] = DB.query(
"SELECT course_id, group_selection, semester_id, name, description, visible FROM course WHERE name like ?" + (if (ignoreHidden) " AND visible = 1" else ""),
"SELECT course_id, group_selection, semester_id, name, description, visible, " +
"submission_mode FROM course WHERE name like ?" + (if (ignoreHidden) " AND visible = 1" else ""),
(res, _) => parseResult(res), "%" + pattern + "%")

/**
Expand All @@ -45,7 +46,7 @@ class CourseService {
* @return The found course
*/
def find(id: Int): Option[Course] = DB.query(
"SELECT course_id, group_selection, semester_id, name, description, visible, group_selection FROM course WHERE course_id = ?",
"SELECT course_id, group_selection, semester_id, name, description, visible, group_selection, submission_mode FROM course WHERE course_id = ?",
(res, _) => parseResult(res), id).headOption

/**
Expand All @@ -55,8 +56,8 @@ class CourseService {
* @return The created course with id
*/
def create(course: Course): Course = {
DB.insert("INSERT INTO course (semester_id, name, description, visible) VALUES (?,?,?,?);",
course.semesterId.orNull, course.name, course.description, course.visible)
DB.insert("INSERT INTO course (semester_id, name, description, visible, submission_mode) VALUES (?,?,?,?,?);",
course.semesterId.orNull, course.name, course.description, course.visible, course.submissionMode)
.map(gk => gk(0).asInstanceOf[BigInteger].intValue())
.flatMap(id => find(id)) match {
case Some(course) => course
Expand All @@ -72,8 +73,8 @@ class CourseService {
* @return True if successful
*/
def update(cid: Int, course: Course): Boolean = {
1 == DB.update("UPDATE course SET semester_id = ?, name = ?, description = ?, visible = ? WHERE course_id = ?",
course.semesterId.orNull, course.name, course.description, course.visible, cid)
1 == DB.update("UPDATE course SET semester_id = ?, name = ?, description = ?, visible = ?, submission_mode = ? WHERE course_id = ?",
course.semesterId.orNull, course.name, course.description, course.visible, course.submissionMode, cid)
}

/**
Expand All @@ -90,6 +91,7 @@ class CourseService {
name = res.getString("name"),
description = res.getString("description"),
visible = res.getBoolean("visible"),
submissionMode = res.getString("submission_mode"),
id = res.getInt("course_id")
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package de.thm.ii.fbs.services.security

import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
import org.springframework.security.web.util.matcher.IpAddressMatcher

@Component
class IpService {
@Value("${services.ip.vpnCidrs}") private var vpnIpRangeString: String = ""
@Value("${services.ip.localCidrs}") private var localIpRangeString: String = ""

private def vpnIpRange: Seq[String] = vpnIpRangeString.split(",").filter(str => str.nonEmpty)
private def localIpRange: Seq[String] = localIpRangeString.split(",").filter(str => str.nonEmpty)

private def isInCidr(cidr: String, ip: String): Boolean = {
new IpAddressMatcher(cidr).matches(ip)
}

private def ipInCidrs(cirds: Seq[String], ip: String): Boolean =
cirds.exists(cidr => isInCidr(cidr, ip))

def isIpInZone(zone: String, ip: String): Boolean =
zone match {
case "internet" => true
case "vpn" => ipInCidrs(vpnIpRange, ip) || ipInCidrs(localIpRange, ip)
case "local" => ipInCidrs(localIpRange, ip) && !ipInCidrs(vpnIpRange, ip)
}
}

0 comments on commit 1b041e5

Please sign in to comment.