Skip to content

Commit

Permalink
Merge pull request #108 from NIAEFEUP/feature/exchange
Browse files Browse the repository at this point in the history
Feature/exchange
  • Loading branch information
tomaspalma authored Dec 30, 2024
2 parents 48dde7b + 30a7f13 commit 7d8a98d
Show file tree
Hide file tree
Showing 38 changed files with 1,743 additions and 50 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,15 @@ postgres/sql/01_data.sql
# django
django/**/migrations/**
django/university/models.py
django/.env.dev

nginx/certs/*.key

# celery
django/celerybeat-schedule

# stats related graphs
django/university/stats_graphs/*

httpd/shibboleth/*.pem

13 changes: 0 additions & 13 deletions django/.env.dev

This file was deleted.

28 changes: 28 additions & 0 deletions django/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
DEBUG=1
SECRET_KEY=foo

JWT_KEY=change_in_production

DOMAIN=http://localhost:3100/

POSTGRES_DB=tts
POSTGRES_USER=root
POSTGRES_PASSWORD=root
POSTGRES_HOST=db
POSTGRES_PORT=5432

TTS_REDIS_HOST=tts_redis
TTS_REDIS_PORT=6379
TTS_REDIS_USERNAME=
TTS_REDIS_PASSWORD=

SENDER_EMAIL_ADDRESS=
STUDENT_EMAIL_DOMAIN=@up.pt

VERIFY_EXCHANGE_TOKEN_EXPIRATION_SECONDS=86400

OIDC_RP_CLIENT_ID=
OIDC_RP_CLIENT_SECRET=

SIGARRA_USERNAME=
SIGARRA_PASSWORD=
29 changes: 17 additions & 12 deletions django/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,31 +1,36 @@
#!/bin/sh
#!/bin/sh

# WARNING: The script will not work if formated with CRLF.
# WARNING: The script will not work if formated with CRLF.

# Configure the shell behaviour.
# Configure the shell behaviour.
set -e
if [[ "${DEBUG}" == 1 ]]
then set -x
fi
if [[ "${DEBUG}" == 1 ]]; then
set -x
fi

# Get parameters.
cmd="$@"

# Waits for PostgreSQL initialization.
# Waits for PostgreSQL initialization.
until PGPASSWORD="${POSTGRES_PASSWORD}" psql -h "${POSTGRES_HOST}" -U "${POSTGRES_USER}" "${POSTGRES_DB}" -c 'select 1'; do
>&2 echo "PostgreSQL is unavailable - sleeping"
sleep 4
>&2 echo "PostgreSQL is unavailable - sleeping"
sleep 4
done
>&2 echo "PostgreSQL is up - executing command"
>&2 echo "PostgreSQL is up - executing command"

echo "ENTRYPOINT RAN"

# Migrate the Django.
python manage.py inspectdb > university/models.py
python manage.py makemigrations
python manage.py migrate
python manage.py migrate --fake sessions zero
python manage.py migrate university --fake
python manage.py migrate --fake-initial
python manage.py inspectdb >university/models.py

# Initialize redis worker for celery and celery's beat scheduler in the background
celery -A tasks worker --loglevel=INFO &
celery -A tasks beat &

# Initializes the API.
# Initializes the API.
exec $cmd
1 change: 1 addition & 0 deletions django/entrypoint_prod.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ done
# Migrate the Django.
python manage.py inspectdb >university/models.py
python manage.py makemigrations
python manage.py migrate
python manage.py migrate university --fake

python manage.py runserver 0.0.0.0:8000
3 changes: 3 additions & 0 deletions django/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ psycopg2==2.9.9
celery==5.2.7
redis==3.5.3
python-dotenv==1.0.1
requests==2.31.0
pyjwt==2.8.0
mozilla-django-oidc==4.0.1
channels==4.1.0
daphne==4.1.2
python-socketio==5.11.4
78 changes: 70 additions & 8 deletions django/tts_be/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@

from pathlib import Path
import os
from dotenv import dotenv_values


CONFIG={
**dotenv_values(".env"), # load variables
**os.environ, # override loaded values with environment variables
}

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
Expand All @@ -22,12 +27,21 @@
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/

SECRET_KEY = os.getenv('SECRET_KEY')
SECRET_KEY = CONFIG['SECRET_KEY']

JWT_KEY= CONFIG['JWT_KEY']

DEBUG = os.getenv('DEBUG')
DEBUG = int(DEBUG) != 0 if DEBUG else False

ALLOWED_HOSTS = ['0.0.0.0', 'localhost', 'tts.niaefeup.pt', 'tts-staging.niaefeup.pt']
DOMAIN = os.getenv('DOMAIN')
DEBUG = False if int(CONFIG['DEBUG']) == 0 else True

ALLOWED_HOSTS = ['tts.niaefeup.pt', 'tts-staging.niaefeup.pt']

if DEBUG:
ALLOWED_HOSTS.extend(['localhost', 'tts-dev.niaefeup.pt'])


# Application definition

Expand All @@ -37,10 +51,11 @@
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
#'django.contrib.sessions', # legacy
'django.contrib.sessions', # legacy
'django.contrib.messages',
'rest_framework',
'django.contrib.staticfiles',
'mozilla_django_oidc',
'university',
'channels',
]
Expand All @@ -51,18 +66,24 @@
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'university.auth_middleware.AuthMiddleware',
'mozilla_django_oidc.middleware.SessionRefresh',
'django.middleware.csrf.CsrfViewMiddleware'
]

SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

ROOT_URLCONF = 'tts_be.urls'

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'DIRS': [
os.path.join(BASE_DIR, 'university/exchange/emails')
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
Expand All @@ -75,10 +96,35 @@
},
]



WSGI_APPLICATION = 'tts_be.wsgi.application'

ASGI_APPLICATION = 'tts_be.asgi.application'
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'university.auth.CustomOIDCAuthentationBackend'
)

OIDC_RP_CLIENT_ID = os.environ['OIDC_RP_CLIENT_ID']
OIDC_RP_CLIENT_SECRET = os.environ['OIDC_RP_CLIENT_SECRET']
OIDC_RP_SIGN_ALGO = "RS256"

OIDC_STORE_ID_TOKEN = True
OIDC_STORE_ACCESS_TOKEN = True

OIDC_OP_AUTHORIZATION_ENDPOINT = "https://open-id.up.pt/realms/sigarra/protocol/openid-connect/auth"
OIDC_OP_TOKEN_ENDPOINT = "https://open-id.up.pt/realms/sigarra/protocol/openid-connect/token"
OIDC_OP_USER_ENDPOINT = "https://open-id.up.pt/realms/sigarra/protocol/openid-connect/userinfo"
OIDC_OP_JWKS_ENDPOINT = "https://open-id.up.pt/realms/sigarra/protocol/openid-connect/certs"
OIDC_OP_LOGOUT_ENDPOINT = "https://open-id.up.pt/realms/sigarra/protocol/openid-connect/logout"

OIDC_RP_SCOPES = "openid email profile uporto_data"

LOGIN_REDIRECT_URL = "/"

OIDC_RENEW_ID_TOKEN_EXPIRY_SECONDS = 3600 * 60

ASGI_APPLICATION = 'tts_be.asgi.application'

# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases
Expand Down Expand Up @@ -125,7 +171,6 @@

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.0/howto/static-files/

Expand All @@ -144,7 +189,24 @@
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
]
}

CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': f"redis://{CONFIG['TTS_REDIS_HOST']}:{CONFIG['TTS_REDIS_PORT']}//"
}
}

CORS_ORIGIN_ALLOW_ALL = True
CORS_ORIGIN_ALLOW_ALL = bool(DEBUG)
CORS_ALLOW_CREDENTIALS = True
CORS_ALLOW_HEADERS = [
"X-CSRFToken"
]

VERIFY_EXCHANGE_TOKEN_EXPIRATION_SECONDS = int(os.getenv("VERIFY_EXCHANGE_TOKEN_EXPIRATION_SECONDS", 3600 * 24))

EMAIL_HOST = os.getenv("EMAIL_HOST", "tts-mailpit")
EMAIL_PORT = os.getenv("EMAIL_PORT", 1025)
EMAIL_HOST_USER = os.getenv("EMAIL_HOST_USER", None)
EMAIL_HOST_PASSWORD = os.getenv("EMAIL_HOST_PASSWORD", None)
3 changes: 0 additions & 3 deletions django/university/admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +0,0 @@
from django.contrib import admin

# Register your models here.
29 changes: 29 additions & 0 deletions django/university/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from mozilla_django_oidc.auth import OIDCAuthenticationBackend
from university.controllers.SigarraController import SigarraController
from university.controllers.StudentController import StudentController
from university.models import UserCourseUnits, Class

class CustomOIDCAuthentationBackend(OIDCAuthenticationBackend):

def create_user(self, claims):
user = super(CustomOIDCAuthentationBackend, self).create_user(claims)

user.first_name = claims.get('given_name', '')
user.last_name = claims.get('family_name', '').split(' ')[-1]
user.username = claims.get('nmec', '')
user.password = "" # User does not have password
user.save()

StudentController.populate_user_course_unit_data(user.username)

return user

def update_user(self, user, claims):
user.first_name = claims.get('given_name', '')
user.last_name = claims.get('family_name', '').split(' ')[-1]
user.save()

StudentController.populate_user_course_unit_data(user.username, erase_previous=True)

return user

43 changes: 43 additions & 0 deletions django/university/auth_middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import re

from django.http import HttpResponseForbidden

class AuthMiddleware:
def __init__(self, get_response):
self.get_response = get_response
self.auth_paths = [
'/logout/',
'/auth/info/',
'/student/schedule/',
re.compile(r'^/student/\w+/photo/$'),
re.compile(r'^/schedule_sigarra/\d+/$'),
re.compile(r'^/class_sigarra_schedule/\d+/.+/$'),
re.compile(r'^/exchange/marketplace/$'),
re.compile(r'^/exchange/direct/$'),
re.compile(r'^/exchange/options/$'),
'/is_admin/',
'/export/',
'/direct_exchange/history/',
'/marketplace_exchange/',
'/submit_marketplace_exchange/',
]

def __call__(self, request):
in_paths = False

for path in self.auth_paths:
if isinstance(path, str) and request.path == path:
in_paths = True
break
elif isinstance(path, re.Pattern) and path.match(request.path):
in_paths = True
break

if not in_paths:
return self.get_response(request)

if not request.user.is_authenticated:
return HttpResponseForbidden()

return self.get_response(request)

Loading

0 comments on commit 7d8a98d

Please sign in to comment.