diff --git a/DragonBank/Rapport_Projet.md b/DragonBank/Rapport_Projet.md deleted file mode 100644 index 65f71de..0000000 --- a/DragonBank/Rapport_Projet.md +++ /dev/null @@ -1,33 +0,0 @@ -# 🐉 DragonBank - Rapport de Projet - -## 1. Description du Projet et des Conteneurs (Dockers) -Le projet DragonBank repose sur une architecture en micro-services conteneurisĂ©e Ă  l'aide de **Docker** et **Docker-Compose**. L'architecture comprend les 4 conteneurs suivants : - -- **`db` (Postgres)** : Base de donnĂ©es relationnelle persistante contenant les schĂ©mas, les tables, les configurations et l'historique des opĂ©rations. -- **`backend` (Python/Flask)** : Le "cerveau" de l'application. Cette API RESTful gĂšre toute la logique mĂ©tier (authentification, gestion des comptes, validations de virements, audits) en utilisant une architecture saine (*Controllers/Services/DAOs*). -- **`frontend` (Python/Flask + Jinja2)** : L'interface utilisateur web. Elle agit comme un client qui appelle l'API via des requĂȘtes HTTP JSON, et affiche les informations retournĂ©es dans une belle interface graphique dynamique propulsĂ©e par Bootstrap. -- **`interests` (Python)** : (Conteneur Bonus) Un conteneur "worker" autonome qui calcule et distribue le paiement asynchrone des intĂ©rĂȘts journaliers applicables (+3% ou +2%) aux comptes d'Ă©pargne (Livret A, Assurance Vie), sans jamais nĂ©cessiter d'intervention humaine et sans bloquer l'API principale de la banque. - -## 2. Fonctionnement de Chaque Application -- **Frontend** : L'utilisateur se connecte via `/login`. L'application authentifie l'utilisateur, rĂ©cupĂšre le token JWT de l'API et l'enregistre de maniĂšre sĂ©curisĂ©e dans la session locale (`session` en base 64 et signĂ©e). L'interface requiert ce token pour toutes les vues protĂ©gĂ©es afin de communiquer avec l'API (tableaux de bord, liste des comptes, demande de virement interne ou de virement intra-partenaires extĂ©rieurs). -- **Backend API** : L'API intercepte les requĂȘtes JSON. Elle vĂ©rifie l'intĂ©gritĂ© de l'identitĂ© du client via le token JWT, et applique les rĂšgles de validations bancaires (fonds suffisants pour valider l'opĂ©ration de virement, restrictions de l'IBAN bĂ©nĂ©ficiaire, etc.). La gestion transactionnelle de la base de donnĂ©es via nos services assure que l'architecture restera toujours consistante (propriĂ©tĂ©s ACID respectĂ©es). -- **Interests** : Service d'arriĂšre-plan avec un timer. Ce module exĂ©cute le SQL pour rĂ©cupĂ©rer les encours de l'Ă©pargne sur chaque client de DragonBank et gĂ©nĂšre un virement interne spĂ©cial pour matĂ©rialiser ce versement de profit au client de maniĂšre silencieuse. - -## 3. SchĂ©ma de Base de donnĂ©es -Le modĂšle relationnel (cf. `db/init.sql`) a Ă©tĂ© pensĂ© pour maximiser la sĂ©curitĂ© et la traçabilitĂ© de toutes les opĂ©rations de la banque : -- Les tables fondamentales : **`users`** (les clients, avec la civilitĂ© et le mot de passe hashĂ©), **`accounts`** (les comptes bancaires avec association au `user_id` et garantie par une contrainte de balance positive `>= 0`), **`beneficiaries`** (le carnet d'adresses bancaires d'un usager). -- La table **`transactions`** : L'historique immuable de chaque transfert d'argent (qui liste le `from_account`, `to_account`, et le type `virement_interne`, `externe` ou `interets`). -- Les tables annexes de traçabilitĂ© : **`audit_logs`** et **`sessions`** enregistrent toutes les activitĂ©s douteuses (tentatives d'authentifications rĂ©pĂ©tĂ©es, de connexions par d'autres IP, et expiration stricte par token session). - -## 4. Notions de SĂ©curitĂ© ImplĂ©mentĂ©es -Pour garantir la fiabilitĂ© de cet environnement acadĂ©mique, de nombreuses couches sĂ©curitaires ont Ă©tĂ© intĂ©grĂ©es : -1. **Mots de passe Fortement HachĂ©s** : ConservĂ©s cĂŽtĂ© base de donnĂ©es avec `bcrypt` en utilisant des sels d'entropie (pour se prĂ©munir totalement contre les attaques Rainbow Tables). -2. **SystĂšme de Jetons JSON (JWT)** : UtilisĂ©s pour vĂ©rifier la validitĂ© des accĂšs aux points d'API sans requĂ©rir le mot de passe sur les appels courants. -3. **Protection et VĂ©rifications des Profils** : Toute intention de changer un e-mail principal ou un mot de passe bancaire (depuis le nouvel Onglet Profil) exigera nĂ©cessairement de produire son dernier mot de passe actuel en date. -4. **Isolations Docker Secrets (Bonus)** : Pour empĂȘcher la divulgation des identifiants Root de la Base de DonnĂ©es au sein des variables ou du code source, Postgres s'initialise au lancement en lisant le mot de passe confinĂ© au sein d'un "Docker Secret" (`db_password.txt`). - -## 5. Explication de l'implĂ©mentation Docker-Compose -Le fichier `docker-compose.yml` construit le rĂ©seau total de maniĂšre hermĂ©tique et orchestrĂ©e : -- **Networks (Cloisonnement)** : Un rĂ©seau de Frontend (`dragonbank-frontend-net`) et un rĂ©seau de Backend (`dragonbank-backend-net`). Ce bridage fait que le Frontend a le droit de discuter avec l'API, mais empĂȘche physiquement le Frontend et potentiellement des attaques externes de ping la Base de DonnĂ©es directement. -- **Volumes** : Un volume permanent `postgres_data` permet un maintien et une persistance sans perte des transactions et liquiditĂ©s de la banque mĂȘme si le conteneur subit un crash inopinĂ© ou une mise Ă  jour. -- **Healthchecks (Bonus)** : La politique adoptĂ©e est "Fail Fast / Recover Quick". Les configurations Healthcheck imposent un ordre strict au dĂ©marrage absolu : l'interface Frontend s'interdira de dĂ©marrer et restera en pause tant que la route Health API de l'architecture Backend Python n'aura pas donnĂ© son aval (`curl -f /api/health`). diff --git a/DragonBank/backend/services/compte_service.py b/DragonBank/backend/services/compte_service.py index e4d2acd..d8340ef 100644 --- a/DragonBank/backend/services/compte_service.py +++ b/DragonBank/backend/services/compte_service.py @@ -20,18 +20,60 @@ class CompteService: conn = creer_connexion() try: import decimal as _d + type_compte = donnees.get('account_type', 'courant') depot_brut = donnees.get('initial_deposit') depot = _d.Decimal(str(depot_brut)) if depot_brut else _d.Decimal('0') if depot < 0: raise ValueError('Le depot initial ne peut pas etre negatif') + # Pour les comptes epargne, le depot est preleve du compte courant + if type_compte in ('livret_a', 'assurance_vie') and depot > 0: + # Trouver le compte courant actif de l'utilisateur + comptes = self.compte_dao.lister_par_utilisateur(conn, id_utilisateur_courant) + compte_courant = next( + (c for c in comptes if c['account_type'] == 'courant'), None + ) + if not compte_courant: + raise ValueError( + "Vous devez possĂ©der un compte courant pour alimenter ce compte." + ) + solde_courant = _d.Decimal(str(compte_courant['balance'])) + if solde_courant < depot: + raise ValueError( + f"Solde insuffisant sur votre compte courant " + f"(disponible : {float(solde_courant):.2f} €)." + ) + # Debiter le compte courant + self.compte_dao.debiter(conn, str(compte_courant['id']), depot) + + # Enregistrer la transaction de transfert vers le nouveau compte (fait apres ouvrir) + id_courant = str(compte_courant['id']) + else: + id_courant = None + compte = self.compte_dao.ouvrir( conn, id_utilisateur_courant, - donnees.get('account_type', 'courant'), + type_compte, depot ) + + # Si on a debite le courant, creer la transaction de transfert + if id_courant and depot > 0: + curseur = conn.cursor() + noms = {'livret_a': 'Livret A', 'assurance_vie': 'Assurance Vie'} + libelle = f"Versement initial sur {noms.get(type_compte, type_compte)}" + curseur.execute( + """ + INSERT INTO transactions + (from_account_id, to_account_id, transaction_type, + amount, description, status, executed_at) + VALUES (%s, %s, 'virement_interne', %s, %s, 'completed', NOW()) + """, + (id_courant, str(compte['id']), float(depot), libelle) + ) + enregistrer_audit(conn.cursor(), id_utilisateur_courant, 'OPEN_ACCOUNT', - {'type': donnees.get('account_type'), 'depot': float(depot)}, + {'type': type_compte, 'depot': float(depot)}, obtenir_ip_client()) conn.commit() return compte diff --git a/DragonBank/docker-compose.yml b/DragonBank/docker-compose.yml index b02d8e3..baf76ba 100644 --- a/DragonBank/docker-compose.yml +++ b/DragonBank/docker-compose.yml @@ -84,7 +84,7 @@ services: POSTGRES_PASSWORD_FILE: /run/secrets/db_password INTEREST_RATE_LIVRET_A: 0.03 INTEREST_RATE_ASSURANCE_VIE: 0.02 - INTERVAL_SECONDS: 60 + INTERVAL_SECONDS: 900 secrets: - db_password depends_on: diff --git a/DragonBank/frontend/app.py b/DragonBank/frontend/app.py index fff3ee3..11aadf3 100644 --- a/DragonBank/frontend/app.py +++ b/DragonBank/frontend/app.py @@ -234,26 +234,41 @@ def accounts(): ) +# Labels lisibles pour les types de comptes +ACCOUNT_LABELS = { + 'courant': 'Compte Courant', + 'livret_a': 'Livret A', + 'assurance_vie': 'Assurance Vie', +} + + @app.route('/accounts/open', methods=['GET', 'POST']) @login_required def open_account(): """Ouverture d'un nouveau compte.""" + token = session['token'] + accounts_data, _ = api_request('GET', '/api/accounts', token=token) + accounts_list = accounts_data.get('accounts', []) + if request.method == 'POST': - token = session['token'] + type_compte = request.form.get('account_type') form_data = { - 'account_type': request.form.get('account_type'), + 'account_type': type_compte, 'initial_deposit': float(request.form.get('initial_deposit', 0)) } - + data, status = api_request('POST', '/api/accounts', data=form_data, token=token) - + if status == 201: - flash(f'Compte {form_data["account_type"]} ouvert avec succĂšs ! 🎉', 'success') + label = ACCOUNT_LABELS.get(type_compte, type_compte) + flash(f'{label} ouvert avec succĂšs ! 🎉', 'success') return redirect(url_for('accounts')) else: - flash(data.get('error', 'Erreur lors de l\'ouverture du compte'), 'danger') - - return render_template('open_account.html', user=session.get('user', {})) + flash(data.get('error', "Erreur lors de l'ouverture du compte"), 'danger') + + return render_template('open_account.html', + user=session.get('user', {}), + accounts=accounts_list) # ============================================ diff --git a/DragonBank/frontend/templates/open_account.html b/DragonBank/frontend/templates/open_account.html index 3e63a89..efd75ad 100644 --- a/DragonBank/frontend/templates/open_account.html +++ b/DragonBank/frontend/templates/open_account.html @@ -11,6 +11,18 @@
+ + {% set compte_courant = accounts | selectattr('account_type', 'equalto', 'courant') | list | first %} + {% if compte_courant %} + + {% endif %} +
@@ -18,7 +30,8 @@ -
-
+
- - + + Laissez 0 pour ouvrir le compte sans dépÎt initial.
+ + + @@ -61,7 +81,7 @@
-
+
Compte Courant

Compte de tous les jours pour vos dĂ©penses et virements. Pas d'intĂ©rĂȘts. @@ -73,7 +93,9 @@

Livret A

- Épargne rĂ©glementĂ©e Ă  taux fixe de 3 % par cycle. Un seul Livret A autorisĂ©. + Épargne rĂ©glementĂ©e Ă  taux fixe de 3 % par cycle. + Le dĂ©pĂŽt initial est prĂ©levĂ© sur votre Compte Courant. + Un seul Livret A autorisĂ©.

@@ -82,6 +104,7 @@
Assurance Vie

Placement long terme à 2 % par cycle sur les fonds euros. + Le dépÎt initial est prélevé sur votre Compte Courant. Une seule Assurance Vie autorisée.

@@ -91,3 +114,20 @@
{% endblock %} + +{% block scripts %} + +{% endblock %} + diff --git a/DragonBank/interests/app.py b/DragonBank/interests/app.py index 00b9c16..e735152 100644 --- a/DragonBank/interests/app.py +++ b/DragonBank/interests/app.py @@ -71,8 +71,8 @@ TAUX_LIVRET_A = decimal.Decimal(os.environ.get('INTEREST_RATE_LIVRET_A', '0.03') TAUX_ASSURANCE_VIE = decimal.Decimal(os.environ.get('INTEREST_RATE_ASSURANCE_VIE', '0.02')) # Duree en secondes entre deux cycles de calcul des interets. -# La valeur 86400 correspond a 24 heures. -INTERVALLE_SECONDES = int(os.environ.get('INTERVAL_SECONDS', '86400')) +# La valeur 900 correspond a 15 minutes. +INTERVALLE_SECONDES = int(os.environ.get('INTERVAL_SECONDS', '900')) # Nombre maximum de tentatives de connexion a la DB au demarrage. TENTATIVES_CONNEXION_MAX = int(os.environ.get('DB_RETRY_MAX', '30')) diff --git a/DragonBank/projet_Rapport_Docker_sehl_yolou_val.pdf b/DragonBank/projet_Rapport_Docker_sehl_yolou_val.pdf new file mode 100644 index 0000000..d44faf8 Binary files /dev/null and b/DragonBank/projet_Rapport_Docker_sehl_yolou_val.pdf differ diff --git a/DragonBank/schema/schemabd.png b/DragonBank/schema/schemabd.png new file mode 100644 index 0000000..fb082fc Binary files /dev/null and b/DragonBank/schema/schemabd.png differ