Skip to content

Commit

Permalink
implementing the password update functionality in the user frontend a…
Browse files Browse the repository at this point in the history
…nd making changes to the backend
  • Loading branch information
ooemperor committed Jan 16, 2024
1 parent 0e31192 commit 04f5eb5
Show file tree
Hide file tree
Showing 8 changed files with 253 additions and 25 deletions.
40 changes: 36 additions & 4 deletions codeGrader/backend/api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def index() -> dict:

@app.route("/admin/login", methods=['POST'])
@authentication
def admin_login():
def admin_login() -> dict:
"""
Login for a provided Admin User
"""
Expand All @@ -105,7 +105,7 @@ def admin_login():

@app.route("/user/login", methods=['POST'])
@authentication
def user_login():
def user_login() -> dict:
"""
Login for a provided user
"""
Expand Down Expand Up @@ -135,7 +135,7 @@ def user(id_: int) -> dict:
return UserHandler().delete(id_)


@app.route("/user/<int:id_>/passwordreset", methods=['POST'])
@app.route("/user/<int:id_>/password/reset", methods=['POST'])
@authentication
def user_password_reset(id_: int) -> dict:
"""
Expand All @@ -149,6 +149,22 @@ def user_password_reset(id_: int) -> dict:
return UserPasswordResetHandler().reset(id_)


@app.route("/user/<int:id_>/password/update", methods=['POST'])
@authentication
def user_password_update(id_: int) -> dict:
"""
Route for password reset on a admin user
@param id_: The identifier of the admin user
@type id_: int
@return: Custom Response messgae that we get from the handler class.
@rtype: dict
"""
if request.method == 'POST':
user_id = request.get_json()["id"]
password = request.get_json()["password"]
return UserPasswordResetHandler().change(id_, password)


@app.route("/users", methods=['GET'])
@authentication
def users() -> dict:
Expand Down Expand Up @@ -191,7 +207,7 @@ def admin(id_: int) -> dict:
return AdminUserHandler().delete(id_)


@app.route("/admin/<int:id_>/passwordreset", methods=['POST'])
@app.route("/admin/<int:id_>/password/reset", methods=['POST'])
@authentication
def admin_password_reset(id_: int) -> dict:
"""
Expand All @@ -205,6 +221,22 @@ def admin_password_reset(id_: int) -> dict:
return AdminUserPasswordResetHandler().reset(id_)


@app.route("/admin/<int:id_>/password/update", methods=['POST'])
@authentication
def admin_password_update(id_: int) -> dict:
"""
Route for password reset on a admin user
@param id_: The identifier of the admin user
@type id_: int
@return: Custom Response messgae that we get from the handler class.
@rtype: dict
"""
if request.method == 'POST':
user_id = request.get_json()["id"]
password = request.get_json()["password"]
return AdminUserPasswordResetHandler().change(id_, password)


@app.route("/admins", methods=['GET'])
@authentication
def admins() -> dict:
Expand Down
4 changes: 2 additions & 2 deletions codeGrader/backend/api/handlers/Base.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def _preprocess_data_dict(self, dict_: dict):
# this allows to overwrite the method in child classes
return dict_

def get(self, id_: int):
def get(self, id_: int) -> dict:
"""
Get a specific Object from the database from the corresponding table
Will be overwritten in some of the subclasses for better assertions and preprocessing
Expand Down Expand Up @@ -146,7 +146,7 @@ def put(self, id_: int, dict_: dict):
except Exception as err:
return self.create_generic_error_response('PUT', err, id_)

def delete(self, id_: int):
def delete(self, id_: int) -> dict:
"""
Deleting a object from the database
@param id_: The identifier of the object
Expand Down
33 changes: 28 additions & 5 deletions codeGrader/backend/api/handlers/PasswordReset.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ def __init__(self) -> None:
"""
super().__init__()
self.password = None
self.generate_random_password()

def generate_random_password(self):
char_list = string.ascii_letters + string.digits + "!-_.?/"
Expand All @@ -56,9 +55,9 @@ def generate_random_password(self):

self.password = new_password

def reset(self, id_: int) -> dict:
def _update_password(self, id_):
"""
Reset function on the User Object
Update the password on the User Object in the database
@param id_: the id of the user in the database
@type id_: int
@return: A Dictionary reporting success or failure
Expand All @@ -76,11 +75,35 @@ def reset(self, id_: int) -> dict:
print(self.password)
self.sql_session.update(self.dbClass, user.id, user_dict)

return self.create_generic_response('POST', "Password has been reset", password=self.password)
return self.create_generic_response('POST', "Password has been changed", password=self.password)

except Exception as e:
print(e)
return self.create_generic_error_response('POST', "Password Reset failed for User")
return self.create_generic_error_response('POST', "Password Change failed for User")

def reset(self, id_: int) -> dict:
"""
Reset function on the User Object
@param id_: the id of the user in the database
@type id_: int
@return: A Dictionary reporting success or failure
@rtype: dict
"""
self.generate_random_password()
return self._update_password(id_)

def change(self, id_: int, password: str) -> dict:
"""
Update a password for a given user object
@param id_: the id of the user in the database
@type id_: int
@param password: The password that shall be set
@type password: str
@return: A Dictionary reporting success or failure
@rtype: dict
"""
self.password = password
return self._update_password(id_)


class AdminUserPasswordResetHandler(PasswordResetHandler):
Expand Down
15 changes: 13 additions & 2 deletions codeGrader/frontend/user/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
from codeGrader.frontend.user import templates
from codeGrader.frontend.user.handlers import UserSessionHandler, SessionUser, UserLoginHandler, HomeHandler, \
ExerciseListHandler, ExerciseHandler, TaskHandler, TaskListHandler, TaskAttachmentHandler, TaskInstructionHandler, \
AddSubmissionHandler, Settingshandler
AddSubmissionHandler, SettingsHandler, PasswordResetHandler
from gevent.pywsgi import WSGIServer
from typing import Union
import datetime
Expand Down Expand Up @@ -217,7 +217,18 @@ def addSubmission(task_id_: int) -> Union[Response, str]:
@login_required
def settings():
if request.method == 'GET':
return Settingshandler(request).get()
return SettingsHandler(request).get()


@app.route("/passwordreset", methods=['POST'])
@login_required
def password_reset() -> Response:
"""
Route to reset a password of a user that is already logged in
@return: Redirect back to the settings page with a distinct flash messaage
"""
if request.method == 'POST':
return PasswordResetHandler(request).post()


def user_frontend():
Expand Down
84 changes: 84 additions & 0 deletions codeGrader/frontend/user/handlers/PasswordReset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# CodeGrader - https://github.com/ooemperor/CodeGrader
# Copyright © 2023, 2024 Michael Kaiser <michael.kaiser@emplabs.ch>
#
# 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 <http://www.gnu.org/licenses/>.

"""
Handler Class for the Resetting the password of a user in the user frontend
"""

import flask
from flask import request, render_template, redirect, url_for, flash, Response
from .Base import BaseHandler
from typing import Union
import json


class PasswordResetHandler(BaseHandler):
"""
Handler Class for the PasswordReset
"""

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 post(self) -> Union[str, Response]:
"""
Post method to reset the password of the user via an api call to the backend
@return: The rendered site or a redirect
@rtype: str|Response
"""

user = self.user
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.user.username
response = self.api.post('/user/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.user.id
# resetting the password
response = self.api.post(f"/user/{self.user.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"))
2 changes: 1 addition & 1 deletion codeGrader/frontend/user/handlers/Settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from typing import Union


class Settingshandler(BaseHandler):
class SettingsHandler(BaseHandler):
"""
Handler Class for the Settings
"""
Expand Down
5 changes: 3 additions & 2 deletions codeGrader/frontend/user/handlers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@
from .TaskAttachment import TaskAttachmentHandler
from .TaskInstruction import TaskInstructionHandler
from .Submission import AddSubmissionHandler
from .Settings import Settingshandler
from .Settings import SettingsHandler
from .PasswordReset import PasswordResetHandler

__all__ = ["BaseHandler", "UserSessionHandler", "SessionUser", "UserLoginHandler", "HomeHandler", "ExerciseListHandler",
"ExerciseHandler", "TaskHandler", "TaskListHandler", "TaskAttachmentHandler", "TaskInstructionHandler",
"AddSubmissionHandler", "Settingshandler"]
"AddSubmissionHandler", "SettingsHandler", "PasswordResetHandler"]
Loading

0 comments on commit 04f5eb5

Please sign in to comment.