""" 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))