This repository has been archived by the owner on Sep 27, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into language-settings
- Loading branch information
Showing
43 changed files
with
1,009 additions
and
84 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,120 @@ | ||
# UGent-2 | ||
[mockup](https://www.figma.com/file/py6Qk9lgFtzbCy9by2qsYU/SELab2?type=design&node-id=617%3A4348&mode=design&t=N4FQR50wAYEyG8qx-1) | ||
De mockup van ons project kan [hier](https://www.figma.com/file/py6Qk9lgFtzbCy9by2qsYU/SELab2?type=design&node-id=617%3A4348&mode=design&t=N4FQR50wAYEyG8qx-1) | ||
gevonden worden. | ||
|
||
## Project setup | ||
|
||
1. Clone de repository naar je lokale machine met het volgende commando: | ||
```bash | ||
git clone https://github.com/SELab-2/UGent-2 | ||
``` | ||
## Backend | ||
|
||
De backend gebruikt Python 3.12. | ||
Volg deze stappen om de backend van het project op te zetten: | ||
|
||
|
||
1. Navigeer naar de backend map: | ||
```bash | ||
cd UGent-2/backend | ||
``` | ||
|
||
2. Start de Python virtual environment: | ||
```bash | ||
python3 -m venv venv | ||
source venv/bin/activate | ||
``` | ||
3. Installeer de benodigde Python packages met behulp van het `requirements.txt` bestand: | ||
```bash | ||
pip install -r requirements.txt | ||
``` | ||
4. Installeer PostgreSQL: | ||
|
||
**Ubuntu** | ||
```bash | ||
sudo apt-get install postgresql postgresql-contrib | ||
``` | ||
**Fedora** | ||
```bash | ||
sudo dnf install postgresql postgresql-server | ||
sudo postgresql-setup --initdb --unit postgresql | ||
sudo systemctl enable --now postgresql | ||
``` | ||
**Arch** | ||
```bash | ||
sudo pacman -S postgresql | ||
sudo su - postgres -c "initdb --locale $LANG -E UTF8 -D '/var/lib/postgres/data'" | ||
sudo systemctl start postgresql.service | ||
sudo systemctl enable postgresql.service | ||
``` | ||
5. Maak een nieuwe database genaamd `delphi` en stel het standaardwachtwoord in: | ||
```bash | ||
sudo -u postgres psql -c "CREATE DATABASE delphi;" | ||
sudo -u postgres psql -c "ALTER USER postgres PASSWORD 'postgres';" | ||
``` | ||
6. Voer het `fill_database_mock.py` script uit om de database te vullen met mock data: | ||
```bash | ||
python fill_database_mock.py | ||
``` | ||
7. Start de API door het `app.py` script uit te voeren: | ||
```bash | ||
python app.py | ||
``` | ||
8. Om meer Info te krijgen over de mogelijke requests die je kan maken naar de API, kan je de swagger documentatie raadplegen op de `/api/docs` route. | ||
9. De testen kunnen uitgevoerd worden met het volgende commando: | ||
```bash | ||
python -m unittest discover tests | ||
``` | ||
|
||
## Frontend | ||
|
||
Volg deze stappen om de backend van het project op te zetten: | ||
|
||
|
||
1. Navigeer naar de backend map: | ||
```bash | ||
cd UGent-2/frontend | ||
``` | ||
2. Installeer Node: | ||
|
||
**Ubuntu** | ||
```bash | ||
sudo apt update | ||
sudo apt install ca-certificates curl gnupg | ||
sudo mkdir -p /etc/apt/keyrings | ||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg | ||
NODE_MAJOR=20 | ||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list | ||
sudo apt update | ||
sudo apt install nodejs | ||
``` | ||
**Fedora** | ||
```bash | ||
sudo dnf install nodejs | ||
``` | ||
**Arch** | ||
```bash | ||
sudo pacman -S nodejs-lts-iron | ||
``` | ||
3. Installeer alle npm dependencies | ||
```bash | ||
npm install | ||
``` | ||
4. Build de frontend: | ||
```bash | ||
npm run build | ||
``` | ||
De gecompileerde html/css/js bevindt zich nu in de `dist` folder | ||
|
||
5. Deploy: | ||
|
||
Zet de inhoud van de `dist` folder op de juiste plaats, zodat het geserveerd kan worden. | ||
|
||
6. De testen kunnen uitgevoerd worden met: (nog niet geïmplementeerd) | ||
```bash | ||
npm run tests | ||
``` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
FROM python:3.12-slim | ||
EXPOSE 8000 | ||
COPY . /backend | ||
WORKDIR /backend | ||
RUN pip install -r requirements.txt | ||
CMD uvicorn --host 0.0.0.0 app:app |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import os | ||
import string | ||
from typing import TYPE_CHECKING | ||
|
||
import httpx | ||
from defusedxml.ElementTree import fromstring | ||
from sqlalchemy.orm import Session | ||
|
||
from domain.logic.student import create_student | ||
from domain.logic.teacher import create_teacher | ||
from domain.logic.user import get_user_with_email | ||
from domain.models.UserDataclass import UserDataclass | ||
|
||
if TYPE_CHECKING: | ||
from _elementtree import Element | ||
|
||
cas_service = os.getenv("CAS_URL", "https://localhost:8080/login") | ||
|
||
|
||
def authenticate_user(session: Session, ticket: str) -> UserDataclass | None: | ||
""" | ||
This function will authenticate the user. | ||
If the use doesn't yet exist in the database, it will create an entry. | ||
a | ||
:param session: Session with the database | ||
:param ticket: A ticket from login.ugent.be/login?service=https://localhost:8080/login | ||
:return: None if the authentication failed, user: UseDataclass is the authentication was successful | ||
""" | ||
allowed_chars = set(string.ascii_letters + string.digits + "-") | ||
if not all(c in allowed_chars for c in ticket): | ||
return None | ||
user_information = httpx.get(f"https://login.ugent.be/serviceValidate?service={cas_service}&ticket={ticket}") | ||
user_dict: dict | None = parse_cas_xml(user_information.text) | ||
if user_dict is None: | ||
return None | ||
|
||
user: UserDataclass | None = get_user_with_email(session, user_dict["email"]) | ||
if user is None: | ||
if user_dict["role"] == "student": | ||
user = create_student(session, user_dict["name"], user_dict["email"]) | ||
elif user_dict["role"] == "teacher": | ||
user = create_teacher(session, user_dict["name"], user_dict["email"]) | ||
return user | ||
|
||
|
||
def parse_cas_xml(xml: str) -> dict | None: | ||
""" | ||
The authentication with CAS returns a xml-object. | ||
This function will read the necessary attributes and return them in a dictionary. | ||
:param xml: str: response xml from CAS | ||
:return: None if the authentication failed else dict | ||
""" | ||
|
||
namespace = "{http://www.yale.edu/tp/cas}" | ||
root: Element | None = fromstring(xml).find(f"{namespace}authenticationSuccess") | ||
if root is None: | ||
return None | ||
user_information: Element | None = root.find(f"{namespace}attributes") | ||
if user_information is None: | ||
return None | ||
givenname: Element | None = user_information.find(f"{namespace}givenname") | ||
surname: Element | None = user_information.find(f"{namespace}surname") | ||
email: Element | None = user_information.find(f"{namespace}mail") | ||
role: list | None = user_information.findall(f"{namespace}objectClass") | ||
if role is not None and givenname is not None and surname is not None and email is not None: | ||
role_str = "" | ||
for r in role: | ||
if r.text == "ugentStudent" and role_str == "": | ||
role_str = "student" | ||
elif r.text == "ugentEmployee": | ||
role_str = "teacher" | ||
|
||
return { | ||
"email": email.text.lower(), | ||
"name": f"{givenname.text} {surname.text}", | ||
"role": role_str, | ||
} | ||
return None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import contextlib | ||
import os | ||
from datetime import UTC, datetime, timedelta | ||
|
||
import jwt | ||
|
||
from domain.models.UserDataclass import UserDataclass | ||
|
||
# Zeker aanpassen in production | ||
jwt_secret = os.getenv("JWT_SECRET", "secret") | ||
|
||
|
||
def verify_token(token: str) -> int | None: | ||
with contextlib.suppress(jwt.ExpiredSignatureError, jwt.DecodeError): | ||
payload = jwt.decode(token, jwt_secret, algorithms=["HS256"]) | ||
return payload.get("uid", None) | ||
|
||
|
||
def create_token(user: UserDataclass) -> str: | ||
expire = datetime.now(UTC) + timedelta(days=1) | ||
to_encode: dict = { | ||
"uid": user.id, | ||
"exp": expire, | ||
} | ||
return jwt.encode(to_encode, jwt_secret, algorithm="HS256") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,23 @@ | ||
class ItemNotFoundError(Exception): | ||
""" | ||
The specified item was not found in the database. | ||
""" | ||
def __init__(self, message: str) -> None: | ||
super().__init__(message) | ||
|
||
|
||
class ActionAlreadyPerformedError(Exception): | ||
""" | ||
The specified action was already performed on the database once before | ||
and may not be performed again as to keep consistency. | ||
""" | ||
def __init__(self, message: str) -> None: | ||
super().__init__(message) | ||
|
||
|
||
class NoSuchRelationError(Exception): | ||
""" | ||
There is no relation between the two specified elements in the database. | ||
""" | ||
def __init__(self, message: str) -> None: | ||
super().__init__(message) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.