diff --git a/README.md b/README.md index 835bafa..c1e5ea2 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,20 @@ # CodeGrader (CG) -This repository will provide the scripts and code for a running Grader. -This will be a web application with User Management and automatic Code Execution and evaluation. +This repository contains the source code for the CodeGrader. +The CodeGrader is an application with User Management and automatic Code Execution and evaluation. For more detailed information on how to setup this Project please visit the doc Folder. -This project is created as a Bachelor Thesis at the University of Bern. +This project is created as a Bachelor Thesis at the University of Bern in 2023/2024. + +## Installation / Deployment +For running the application you can find a detailed Guid in the doc folder for a complete guide. + +## Help +If you have any questions about the application, feel free to get in touch. \ +E-Mail: [michael.kaiser@emplabs.ch](mailto:michael.kaiser@emplabs.ch) + +## License +This application is avaible under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3. diff --git a/codeGrader/backend/api/handlers/Base.py b/codeGrader/backend/api/handlers/Base.py index 726d641..e10821a 100644 --- a/codeGrader/backend/api/handlers/Base.py +++ b/codeGrader/backend/api/handlers/Base.py @@ -176,7 +176,10 @@ def get_all(self, arguments: dict={}) -> dict: objects = self.sql_session.get_all(self.dbClass) if len(objects) == 0: # no data found - return self.create_generic_response('GET', f"No Objects entries to display") + #return self.create_generic_response('GET', f"No Objects entries to display") + output = dict() + output[str(self.dbClass.__table__)] = [] + return output else: object_list = [] diff --git a/codeGrader/backend/config/Config.py b/codeGrader/backend/config/Config.py index f896a67..7d0af33 100644 --- a/codeGrader/backend/config/Config.py +++ b/codeGrader/backend/config/Config.py @@ -38,7 +38,7 @@ def __init__(self): """ self.system = platform.system() if self.system == 'Windows': - configFile = os.path.join(os.path.dirname(__file__), "config.conf") + configFile = os.path.join(os.path.dirname(__file__), "config.sec.conf") code = open(os.path.join(os.path.dirname(__file__), "codeLanguages.json")) elif self.system == 'Linux': configFile = "/etc/codeGrader/backendConfig.conf" diff --git a/codeGrader/backend/config/config.conf b/codeGrader/backend/config/config.conf index e10a51d..ecb9567 100644 --- a/codeGrader/backend/config/config.conf +++ b/codeGrader/backend/config/config.conf @@ -20,18 +20,18 @@ sender = [Database] Dialect = postgresql DBDriver = psycopg2 -Username = codeGrader -Password = codeGrader -Host = 10.101.10.80 +Username = +Password = +Host = 127.0.0.1 Port = 5432 -Database = codeGraderDB +Database = MetaDataColumnsCount = 3 ColumnIgnoreList = [id, creation_dts, updated_dts] [ExecutionService] -Host = 10.101.10.80 +Host = 127.0.0.1 Port = 8003 PathToExecutionFiles = /opt IP_Address_Whitelist = [127.0.0.1] @@ -39,7 +39,7 @@ LXC_Install_Command = -t download -- -r bullseye -a amd64 -d debian [EvaluationService] -Host = 10.101.10.80 +Host = 127.0.0.1 Port = 8002 IP_Address_Whitelist = [127.0.0.1] diff --git a/codeGrader/frontend/admin/app.py b/codeGrader/frontend/admin/app.py index 1e092fc..badeaff 100644 --- a/codeGrader/frontend/admin/app.py +++ b/codeGrader/frontend/admin/app.py @@ -37,7 +37,7 @@ AddTaskInstructionHandler, DeleteTaskInstructionHandler, TaskInstructionHandler, TaskAttachmentHandler, \ SubmissionFileHandler, TestCaseInputFileHandler, TestCaseOutputFileHandler, AddTestCaseHandler, \ DeleteTestCaseHandler, AddMembershipHandler, DeleteMembershipHandler, PasswordResetHandler, AddUserListHandler, \ - ErrorHandler + ErrorHandler, SettingsHandler, AdminPasswordResetHandler from gevent.pywsgi import WSGIServer import datetime @@ -153,6 +153,13 @@ def home() -> str: return HomeHandler(request).get() +@app.route("/settings", methods=['GET']) +@login_required +def settings(): + if request.method == 'GET': + return SettingsHandler(request).get() + + @app.route("/user/", methods=['GET', 'POST']) @login_required def user(id_) -> Union[Response, str]: @@ -231,6 +238,7 @@ def user_password_reset(id_: int) -> Union[Response, str]: return PasswordResetHandler(request).post(id_) + @app.route("/admin/", methods=['GET', 'POST']) @login_required def admin(id_) -> Union[Response, str]: @@ -283,6 +291,19 @@ def deleteAdmin(id_: int) -> Union[Response, str]: return DeleteAdminHandler(request).post(id_) +@app.route("/admin//passsword/reset", methods=['POST']) +@login_required +def admin_password_reset(id_: int) -> Union[Response, str]: + """ + Resetting the password for a admin defined by the id_ + @param id_: The identifier of the object + @type id_: int + @return: Redirect to another view. + """ + if request.method == 'POST': + return AdminPasswordResetHandler(request).post() + + @app.route("/profile/", methods=['GET', 'POST']) @login_required def profile(id_) -> Union[Response, str]: diff --git a/codeGrader/frontend/admin/handlers/PasswordReset.py b/codeGrader/frontend/admin/handlers/PasswordReset.py index 072cebd..361cb79 100644 --- a/codeGrader/frontend/admin/handlers/PasswordReset.py +++ b/codeGrader/frontend/admin/handlers/PasswordReset.py @@ -29,7 +29,7 @@ class PasswordResetHandler(BaseHandler): """ - Handler Class for the PasswordReset + Handler Class for the PasswordReset of a User """ def __init__(self, request: flask.Request) -> None: @@ -61,3 +61,60 @@ def post(self, id_) -> Union[str, Response]: self.flash("You are not allowed to update this user") return redirect(url_for("user", id_=id_)) + + +class AdminPasswordResetHandler(BaseHandler): + """ + Handler Class for the PasswordReset of an Admin User + """ + + def __init__(self, request: flask.Request) -> None: + """ + Constructor for the handler of a PasswordReset of the admin user + @param request: The request provided by the routes file + @type request: flask.Request + @return: Nothing + @rtype: None + """ + super().__init__(request) + + def post(self) -> Union[str, Response]: + """ + Post method to reset the password of the admin via an api call to the backend + @return: The rendered site or a redirect + @rtype: str|Response + """ + + user = self.admin + old_password = self.get_value("old-password") + new_password = self.get_value("new-password") + repeat_new_password = self.get_value("repeat-new-password") + + if new_password != repeat_new_password: + self.flash("The new passwords do not match!") + + elif new_password == repeat_new_password: + body = dict() + body["password"] = old_password + body["username"] = self.admin.username + response = self.api.post('/admin/login', body) + id_ = response["response"]["id"] # is None when the Authentication failed + if id_ is not None: # authentication successful + body["password"] = new_password + body["id"] = self.admin.id + # resetting the password + response = self.api.post(f"/admin/{self.admin.id}/password/update", body) + + if "password" in response.keys(): + # change was successful + self.flash("Password has been changed!") + + else: + # an error occured while changing the password + self.flash( + "An Error occurred in the process of changing the password. Please Contact your administrator!") + else: + self.flash("Your old password is not correct!") + + # finally redirect back to the settings page. + return redirect(url_for("settings")) diff --git a/codeGrader/frontend/admin/handlers/Settings.py b/codeGrader/frontend/admin/handlers/Settings.py new file mode 100644 index 0000000..40bdd2d --- /dev/null +++ b/codeGrader/frontend/admin/handlers/Settings.py @@ -0,0 +1,52 @@ +# CodeGrader - https://github.com/ooemperor/CodeGrader +# Copyright © 2023, 2024 Michael Kaiser +# +# This file is part of CodeGrader. +# +# CodeGrader is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# CodeGrader is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with CodeGrader. If not, see . + +""" +Handler Class for the Settings View of the admin frontend +""" + +import flask +from flask import request, render_template, redirect, url_for, flash, Response +from .Base import BaseHandler +from typing import Union + + +class SettingsHandler(BaseHandler): + """ + Handler Class for the Settings + """ + + def __init__(self, request: flask.Request) -> None: + """ + Constructor for the handler of a single Task + @param request: The request provided by the routes file + @type request: flask.Request + @return: Nothing + @rtype: None + """ + super().__init__(request) + + def get(self) -> Union[str, Response]: + """ + Get Method to render the settings view for the user that is logged in + @return: The rendered site or a redirect + @rtype: str|Response + """ + settings = self.api.get(f"/admin/{self.admin.id}") + + return render_template("settings.html", **settings) diff --git a/codeGrader/frontend/admin/handlers/__init__.py b/codeGrader/frontend/admin/handlers/__init__.py index 840e986..fb1264e 100644 --- a/codeGrader/frontend/admin/handlers/__init__.py +++ b/codeGrader/frontend/admin/handlers/__init__.py @@ -35,8 +35,9 @@ from .Submission import SubmissionFileHandler from .TestCase import TestCaseInputFileHandler, TestCaseOutputFileHandler, AddTestCaseHandler, DeleteTestCaseHandler from .Membership import AddMembershipHandler, DeleteMembershipHandler -from .PasswordReset import PasswordResetHandler +from .PasswordReset import PasswordResetHandler, AdminPasswordResetHandler from .Error import ErrorHandler +from .Settings import SettingsHandler __all__ = ["AdminUserLoginHandler", "AdminSessionHandler", "SessionAdmin", "UserListHandler", "UserHandler", "HomeHandler", "AdminHandler", "AdminListHandler", "ProfileListHandler", "ProfileHandler", @@ -48,4 +49,4 @@ "DeleteTaskInstructionHandler", "TaskInstructionHandler", "TaskAttachmentHandler", "SubmissionFileHandler", "TestCaseInputFileHandler", "TestCaseOutputFileHandler", "AddTestCaseHandler", "DeleteTestCaseHandler", "AddMembershipHandler", "DeleteMembershipHandler", "PasswordResetHandler", "AddUserListHandler", - "ErrorHandler"] + "ErrorHandler", "SettingsHandler", "AdminPasswordResetHandler"] diff --git a/codeGrader/frontend/admin/templates/base.html b/codeGrader/frontend/admin/templates/base.html index fcd052f..f556977 100644 --- a/codeGrader/frontend/admin/templates/base.html +++ b/codeGrader/frontend/admin/templates/base.html @@ -120,6 +120,15 @@

{{ appname }}