Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
23b3fc5
Create login feature with password authentication in the controller :…
hlargitte Jan 29, 2026
09990f6
Create login feature with password authentication in the controller :…
hlargitte Jan 29, 2026
a25adda
Create login feature with password authentication in the controller :…
raynaldlao Jan 29, 2026
83976b7
Create login feature with password authentication in the controller :…
raynaldlao Jan 29, 2026
8560b6a
Create login feature with password authentication in the controller: …
raynaldlao Feb 4, 2026
04b6e06
Create login feature with password authentication in the controller :…
raynaldlao Feb 4, 2026
55fc930
Create login feature with password authentication in the controller :…
raynaldlao Feb 5, 2026
401fd71
Create login feature with password authentication in the controller :…
raynaldlao Feb 5, 2026
363d11d
Create login feature with password authentication in the controller :…
raynaldlao Feb 5, 2026
cca405d
Create login feature with password authentication in the controller :…
raynaldlao Feb 5, 2026
52406c3
Create login feature with password authentication in the controller :…
raynaldlao Feb 5, 2026
d0e2043
Create login feature with password authentication in the controller :…
raynaldlao Feb 5, 2026
d0d636b
Create login feature with password authentication in the controller :…
raynaldlao Feb 5, 2026
68ce539
Create login feature with password authentication in the controller :…
raynaldlao Feb 7, 2026
d693d67
Create login feature with password authentication in the controller :…
raynaldlao Feb 7, 2026
8d0f953
Create login feature with password authentication in the controller :…
raynaldlao Feb 7, 2026
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
24 changes: 15 additions & 9 deletions .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ jobs:
POSTGRES_USER: ${{ secrets.POSTGRES_USER }}
POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }}
POSTGRES_DB: ${{ secrets.POSTGRES_DB }}
# The TEST_DATABASE_URL variable is used in tests/conftest.py

TEST_SECRET_KEY: ${{ secrets.TEST_SECRET_KEY }}
TEST_DATABASE_URL: postgresql://${{ secrets.POSTGRES_USER }}:${{ secrets.POSTGRES_PASSWORD }}@localhost:5432/${{ secrets.POSTGRES_DB }}
TEST_DATABASE_JDBC_URL: jdbc:postgresql://localhost:5432/${{ secrets.POSTGRES_DB }}

Expand All @@ -28,6 +29,11 @@ jobs:
POSTGRES_DB: ${{ secrets.POSTGRES_DB }}
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5

steps:
- name: Checkout code
Expand All @@ -44,17 +50,17 @@ jobs:
- name: Add Poetry to PATH
run: echo "$HOME/.local/bin" >> $GITHUB_PATH

- name: Cache Poetry virtualenv
uses: actions/cache@v4
with:
path: ~/.cache/pypoetry
key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }}
restore-keys: |
${{ runner.os }}-poetry-

- name: Install dependencies
run: poetry install --no-interaction --no-root

- name: Wait for PostgreSQL to be ready
run: |
for i in {1..15}; do
PGPASSWORD="$POSTGRES_PASSWORD" psql -h localhost -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "SELECT 1;" && break
echo "Database not ready yet..."
sleep 2
done

- name: Run Flyway migrations
run: |
docker run --rm \
Expand Down
17 changes: 17 additions & 0 deletions app/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import os
import sys

from flask import Flask

from app.controllers.login import login_bp
from configurations.configuration_variables import env_vars


def initialize_flask_application():
app = Flask(__name__)
if os.getenv("PYTEST_CURRENT_TEST") or "pytest" in sys.modules:
app.secret_key = env_vars.test_secret_key
else:
app.secret_key = env_vars.secret_key
app.register_blueprint(login_bp)
return app
47 changes: 47 additions & 0 deletions app/controllers/login.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from flask import Blueprint, flash, redirect, render_template, request, session, url_for
from sqlalchemy import select
from sqlalchemy.orm import Session

from app.models import Account
from database.database_setup import database_engine

login_bp = Blueprint("auth", __name__)

@login_bp.route("/")
def render_login_page():
return render_template("login.html")


@login_bp.route("/login", methods=["POST"])
def login_authentication():
username = request.form.get("username")
password = request.form.get("password")

with Session(database_engine) as db_session:
query = select(Account).where(Account.account_username == username)
user = db_session.execute(query).scalar_one_or_none()

if user and user.account_password == password:
session["user_id"] = user.account_id
session["username"] = user.account_username
return redirect(url_for("auth.dashboard"))

flash("Incorrect username or password.")
return redirect(url_for("auth.render_login_page"))


@login_bp.route("/dashboard")
def dashboard():
if "user_id" not in session:
return redirect(url_for("auth.render_login_page"))

return (
f"<h1>Welcome {session['username']}</h1>"
"<a href='/logout'>Logout</a>"
)


@login_bp.route("/logout")
def logout():
session.clear()
return redirect(url_for("auth.render_login_page"))
4 changes: 2 additions & 2 deletions app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
Text,
func,
)
from sqlalchemy.orm import declarative_base, relationship
from sqlalchemy.orm import relationship

Base = declarative_base()
from database.database_setup import Base


class Account(Base):
Expand Down
31 changes: 31 additions & 0 deletions app/templates/login.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!doctype html>
<html>
<head>
<title>Connexion Blog</title>
</head>
<body>
<div style="width: 300px; margin: 50px auto">
<h2>Connexion</h2>
{% with messages = get_flashed_messages() %} {% if messages %}
<p style="color: red">{{ messages[0] }}</p>
{% endif %} {% endwith %}
<form action="/login" method="POST">
<input
type="text"
name="username"
placeholder="Utilisateur"
required
style="width: 100%; margin-bottom: 10px"
/>
<input
type="password"
name="password"
placeholder="Mot de passe"
required
style="width: 100%; margin-bottom: 10px"
/>
<button type="submit" style="width: 100%">Se connecter</button>
</form>
</div>
</body>
</html>
34 changes: 34 additions & 0 deletions configurations/configuration_variables.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import os
from pathlib import Path

from dotenv import load_dotenv

BASE_DIR = Path(__file__).resolve().parent.parent

load_dotenv(BASE_DIR / ".env")
load_dotenv(BASE_DIR / ".env.test")

def get_env_variable(name: str):
value = os.getenv(name)
if not value:
raise RuntimeError(f"Missing mandatory environment variable: '{name}'")
return value

class EnvVariablesConfig:
@property
def database_url(self):
return get_env_variable("DATABASE_URL")

@property
def secret_key(self):
return get_env_variable("SECRET_KEY")

@property
def test_database_url(self):
return get_env_variable("TEST_DATABASE_URL")

@property
def test_secret_key(self):
return get_env_variable("TEST_SECRET_KEY")

env_vars = EnvVariablesConfig()
15 changes: 15 additions & 0 deletions database/database_setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import os
import sys

from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base

from configurations.configuration_variables import env_vars

if os.getenv("PYTEST_CURRENT_TEST") or "pytest" in sys.modules:
database_url = env_vars.test_database_url
else:
database_url = env_vars.database_url

database_engine = create_engine(database_url)
Base = declarative_base()
Loading