Skip to content

Add files via upload #5

Open
wants to merge 1 commit into
base: Gavin's-work
Choose a base branch
from
Open
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
Empty file added 5005CMD_new/__init__.py
Empty file.
Binary file added 5005CMD_new/__pycache__/__init__.cpython-310.pyc
Binary file not shown.
Binary file modified 5005CMD_new/__pycache__/newApp.cpython-310.pyc
Binary file not shown.
Binary file modified 5005CMD_new/instance/komodo_hub.db
Binary file not shown.
7 changes: 5 additions & 2 deletions 5005CMD_new/newApp.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# * Gavin's Code *

# Import the required modules for functionality
from flask import Flask, render_template, request, redirect, url_for, flash, abort, jsonify
from flask_sqlalchemy import SQLAlchemy
Expand Down Expand Up @@ -185,11 +187,12 @@ def send_message():

recipientID = request.form.get('recipientID') # Retrieve the 'recipientID' from form and it should correspond to a valid user in the system that is logged in
content = request.form.get('content').strip() # Remove any leading/trailing whitespace
print(f"➡️ Incoming Message Data: recipientID={recipientID}, content={content}")
if not recipientID or not content: # These are used to validate input - make sure not empty/ NULL
flash("Recipient and content are required.", "danger") # If it proves to be empty, it shows a flash message to the user indicating the error
return redirect(url_for('messages')) # Redirect the user back to the messaging dashboard

recipient_user = User.query.get(recipientID)
recipient_user = db.session.get(User, recipientID)
if (not recipient_user or
recipient_user.id == current_user.id or
recipient_user.accountType == "public"): # Make absoloute sure on account type message account restrictions
Expand All @@ -198,6 +201,7 @@ def send_message():

new_message = Message(senderID=current_user.id, recipientID=recipientID, content=content) # Security check to ensure confidentiality and protection - verify that the sender is a teacher and the recipient is an enrolled student [*mod*]
db.session.add(new_message) # Add the new message to the database and add it
print(f"Message saved: {new_message}")
db.session.commit()
flash("Message sent successfully!", "success") # This is a flash message which indicates the message was sent successfully
return redirect(url_for('messages')) # *Redirect*
Expand Down Expand Up @@ -321,6 +325,5 @@ def add_security_headers(response):
# Run Flask
if __name__ == '__main__':
with app.app_context():

db.create_all() # Ensures database & tables exist
app.run(debug=True)
Binary file not shown.
Binary file not shown.
Binary file not shown.
28 changes: 28 additions & 0 deletions 5005CMD_new/testing/test_authentication.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import pytest
import sys
import os
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__) + "/..")) # Finds 'newApp.py' file with absoloute path
from newApp import app, db
from newApp import User # Import the User model for testing
from werkzeug.security import check_password_hash # Security test

@pytest.fixture
def client():
app.config['TESTING'] = True # Emulates a test client anf creates an in-memory database for testing
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:' # Here is a temporary database for tests
client = app.test_client()
with app.app_context():
db.create_all() # Create tables in test database
yield client
db.session.remove()
db.drop_all() # Clean up after tests

def test_registration(client):
inputVal = client.post('/register', data={ # Test the user's registration
'username': 'testuser', # Inpute details[...]
'password': 'Test1234',
'accountType': 'student'},
follow_redirects=True)
user = User.query.filter_by(username="testuser").first()
assert user is not None # Ensures the user is created
assert check_password_hash(user.password, "Test1234") # Verify password hashing for utmost security implementation
53 changes: 53 additions & 0 deletions 5005CMD_new/testing/test_msging_feature.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from newApp import app, db, Message, User
import pytest
from werkzeug.security import generate_password_hash # Import password hashing utility as this prevents test case pass (debug)

@pytest.fixture
def client():
app.config['TESTING'] = True # Setting uo a test client with a standalone database
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:' # Again use an in-memory database for testing
client = app.test_client()
with app.app_context():
db.create_all()
yield client
db.session.remove()
db.drop_all()

def test_send_message(client):
""" Test sending a message and ensuring it appears in recipient's inbox """
with app.app_context():
sender = User(username="sender", password=generate_password_hash("TestPassw0rd1234"), accountType="teacher") # User with hashed pass
recipient = User(username="recipient", password=generate_password_hash("TestPassw0rd1234"), accountType="student") # Second user with hashed pass
db.session.add(sender)
db.session.add(recipient)
db.session.flush() # Ensure the associated IDs are assigned before commited
db.session.commit()
print(f"Sender ID: {sender.id}, Recipient ID: {recipient.id}")

loginVal = client.post('/login', data={ # Logs in the user before sending a message
'username': sender.username,
'password': "TestPassw0rd1234", # Plain text because hashing happens inside the login function
'accountType': sender.accountType},
follow_redirects=True)
print(f"Login Validation Status: {loginVal.status_code}") # Check 1
print(f"Login Validation Data: {loginVal.data.decode()}") # Check 2
assert loginVal.status_code == 200, "Login failed" # Status repr if login fails
with client.session_transaction() as session: # Simulates session as if user is loggedd in
session["_user_id"] = sender.id # Simulating Flask-Login session
session["_fresh"] = True # Marks session as fresh

response = client.post('/messages/send', data={ # Sends a message once a session has started
'recipientID': str(recipient.id), # Convert to a string
'content': "This is a test message."},
follow_redirects=True)
print(f"Message Send Response Status: {response.status_code}")
print(f"Message Send Response Data: {response.data.decode()}")

db.session.flush() # Here I check the database
db.session.commit() # ^
all_messages = Message.query.all()
print(f"All Messages in database: {all_messages}")
message = Message.query.filter_by(recipientID=recipient.id).first()
print(f"Retrieved Message: {message}") # Success
assert message is not None, "Message was not stored in database" # Assertions
assert message.content == "This is a test message.", "Message content not matched" # Check against my input test
30 changes: 30 additions & 0 deletions 5005CMD_new/testing/test_user_interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from selenium import webdriver
import time

def test_home_page_load():

driver = webdriver.Chrome() # Makes sure webdriver is installed - and the purpose of this is to ensure home page loads correctly
driver.get("http://127.0.0.1:5000") # Open the app URL associated with our system
time.sleep(2.5) # Allows the entire page to load
assert "Komodo Hub" in driver.title # Validates the page title
driver.quit() # Closes the browser

def test_login_redirect():
# Tests the login redirect directly
driver = webdriver.Chrome()
driver.get("http://127.0.0.1:5000") # URL page
loginRedirectButton = driver.find_element("link text", "Login") # Finds the UI with the login button
loginRedirectButton.click()
time.sleep(2) # Allows the page to load
assert "Login" in driver.title # Ensures the login page loads
driver.quit() # Exit to move on

def test_register_redirect():
# Tests the register redirect directly
driver = webdriver.Chrome()
driver.get("http://127.0.0.1:5000") # Opens the home page first
registerRedirectButton = driver.find_element("link text", "Register") # Finds the UI with the register/signup button
registerRedirectButton.click() # Clicks the button
time.sleep(2.5) # Allows 2 seconds for the page to load
assert "Signup" in driver.title, f"Expected 'Signup' in page title, but got '{driver.title}'"
driver.quit() # Close the browser after test has been completed
35 changes: 35 additions & 0 deletions 5005CMD_new/testing/testing_access_control.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from newApp import app, db, User # Import necessary modules from the main application file
import pytest

@pytest.fixture
def client():

app.config['TESTING'] = True # Enable 'Flask' testing mode
client = app.test_client() # Create a test client
with app.app_context():
db.create_all() # Create respective tables in the in-memory database
yield client
db.session.remove() # Clears the database session after each test is done
db.drop_all() # This is done to clean the state for each test after preceding - *remember to remove after in all files *

def test_student_restrictions(client):

student = User(username="student_user", # Create respective info regarding student user account
password="TestPass123",
accountType="student")
db.session.add(student) # Adds the student to the database
db.session.commit()
response = client.get('/teacher_dashboard', follow_redirects=True) # Attempt to access teacher dashboard
assert response.status_code == 403 # Respective response for error code

def test_teacher_access(client):

teacher = User(username="teacher_user", password="TestPass123", accountType="teacher") # Create a teacher user
db.session.add(teacher) # Add the teacher data to the database
db.session.commit() # Commits the changes
response = client.get('/teacher_dashboard', follow_redirects=True) # Attempts to access teacher dashboard
assert response.status_code == 200 # Successful access code

"""" A public user test was not done as there many features not yet implemented so seemed pointless yet
However, this was done to ensure each account type remained standalone and ensures public user account types
will stay secure too considering the security enforecments we have taken """