125 lines
4.1 KiB
Python

"""
Modèles de données SQLAlchemy pour la persistance.
"""
from sqlalchemy import Column, Integer, String, DateTime, Boolean, ForeignKey, Text, LargeBinary
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from datetime import datetime
Base = declarative_base()
class Voter(Base):
"""Électeur - Enregistrement et authentification"""
__tablename__ = "voters"
id = Column(Integer, primary_key=True, index=True)
email = Column(String(255), unique=True, index=True, nullable=False)
password_hash = Column(String(255), nullable=False)
first_name = Column(String(100))
last_name = Column(String(100))
citizen_id = Column(String(50), unique=True) # Identifiant unique (CNI)
# Sécurité
public_key = Column(LargeBinary) # Clé publique ElGamal
has_voted = Column(Boolean, default=False)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relations
votes = relationship("Vote", back_populates="voter")
class Election(Base):
"""Élection - Configuration et paramètres"""
__tablename__ = "elections"
id = Column(Integer, primary_key=True, index=True)
name = Column(String(255), nullable=False)
description = Column(Text)
# Dates
start_date = Column(DateTime, nullable=False)
end_date = Column(DateTime, nullable=False)
# Paramètres cryptographiques
elgamal_p = Column(Integer) # Nombre premier
elgamal_g = Column(Integer) # Générateur
public_key = Column(LargeBinary) # Clé publique pour chiffrement
# État
is_active = Column(Boolean, default=True)
results_published = Column(Boolean, default=False)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relations
candidates = relationship("Candidate", back_populates="election")
votes = relationship("Vote", back_populates="election")
class Candidate(Base):
"""Candidat - Options de vote"""
__tablename__ = "candidates"
id = Column(Integer, primary_key=True, index=True)
election_id = Column(Integer, ForeignKey("elections.id"), nullable=False)
name = Column(String(255), nullable=False)
description = Column(Text)
order = Column(Integer) # Ordre d'affichage
created_at = Column(DateTime, default=datetime.utcnow)
# Relations
election = relationship("Election", back_populates="candidates")
votes = relationship("Vote", back_populates="candidate")
class Vote(Base):
"""Vote - Bulletin chiffré"""
__tablename__ = "votes"
id = Column(Integer, primary_key=True, index=True)
voter_id = Column(Integer, ForeignKey("voters.id"), nullable=False)
election_id = Column(Integer, ForeignKey("elections.id"), nullable=False)
candidate_id = Column(Integer, ForeignKey("candidates.id"), nullable=False)
# Vote chiffré avec ElGamal
encrypted_vote = Column(LargeBinary, nullable=False) # Ciphertext ElGamal
# Preuves
zero_knowledge_proof = Column(LargeBinary) # ZK proof que le vote est valide
ballot_hash = Column(String(64)) # Hash du bulletin pour traçabilité
# Métadonnées
timestamp = Column(DateTime, default=datetime.utcnow)
ip_address = Column(String(45)) # IPv4 ou IPv6
# Relations
voter = relationship("Voter", back_populates="votes")
election = relationship("Election", back_populates="votes")
candidate = relationship("Candidate", back_populates="votes")
class AuditLog(Base):
"""Journal d'audit pour la sécurité"""
__tablename__ = "audit_logs"
id = Column(Integer, primary_key=True, index=True)
action = Column(String(100), nullable=False)
description = Column(Text)
# Qui
user_id = Column(Integer, ForeignKey("voters.id"))
# Quand
timestamp = Column(DateTime, default=datetime.utcnow)
# Métadonnées
ip_address = Column(String(45))
user_agent = Column(String(255))