import os from datetime import datetime, timedelta from flask_sqlalchemy import SQLAlchemy from sqlalchemy import Column, Integer, String, DateTime, Boolean, ForeignKey, Text from sqlalchemy.orm import relationship import bcrypt import ipaddress import html db = SQLAlchemy() class User(db.Model): __tablename__ = 'users' id = Column(Integer, primary_key=True) username = Column(String(100), unique=True, nullable=False) password_hash = Column(String(255), nullable=False) role = Column(String(20), default='user') created_at = Column(DateTime, default=datetime.now) last_login = Column(DateTime, nullable=True) is_active = Column(Boolean, default=True) # ТОЛЬКО ЭТИ ПОЛЯ ДЛЯ ПРОФИЛЯ email = Column(String(255), nullable=True) jabber = Column(String(255), nullable=True) updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now) # Настройки уведомлений notify_email = Column(Boolean, default=False) notify_jabber = Column(Boolean, default=False) secrets = relationship('Secret', back_populates='user', cascade='all, delete-orphan') security_logs = relationship('SecurityLog', back_populates='user', cascade='all, delete-orphan') def set_password(self, password: str): password_bytes = password.encode('utf-8')[:72] salt = bcrypt.gensalt() self.password_hash = bcrypt.hashpw(password_bytes, salt).decode('utf-8') def check_password(self, password: str) -> bool: password_bytes = password.encode('utf-8')[:72] return bcrypt.checkpw(password_bytes, self.password_hash.encode('utf-8')) def to_dict(self): return { 'id': self.id, 'username': self.username, 'role': self.role, 'created_at': self.created_at.isoformat() if self.created_at else None, 'last_login': self.last_login.isoformat() if self.last_login else None, 'is_active': self.is_active, 'email': self.email, 'jabber': self.jabber, 'updated_at': self.updated_at.isoformat() if self.updated_at else None, } class Secret(db.Model): __tablename__ = 'secrets' id = Column(Integer, primary_key=True) secret_id = Column(String(100), unique=True, nullable=False, index=True) user_id = Column(Integer, ForeignKey('users.id', ondelete='CASCADE'), nullable=True) created_at = Column(DateTime, default=datetime.now, index=True) created_ip = Column(String(45), nullable=True) created_user_agent = Column(String(255), nullable=True) viewed_at = Column(DateTime, nullable=True) viewed_ip = Column(String(45), nullable=True) viewed_user_agent = Column(String(255), nullable=True) is_viewed = Column(Boolean, default=False, index=True) view_duration = Column(Integer, nullable=True) request_note = Column(String(500), nullable=True) user = relationship('User', back_populates='secrets') security_logs = relationship('SecurityLog', back_populates='secret', cascade='all, delete-orphan') def to_dict(self): return { 'id': self.id, 'secret_id': self.secret_id, 'user_id': self.user_id, 'created_at': self.created_at.isoformat() if self.created_at else None, 'created_ip': self.created_ip, 'viewed_at': self.viewed_at.isoformat() if self.viewed_at else None, 'viewed_ip': self.viewed_ip, 'is_viewed': self.is_viewed, 'view_duration': self.view_duration, 'request_note': self.request_note } class SecurityLog(db.Model): __tablename__ = 'security_logs' id = Column(Integer, primary_key=True) timestamp = Column(DateTime, default=datetime.now, index=True) ip = Column(String(45), nullable=False, index=True) event_type = Column(String(50), nullable=False, index=True) details = Column(Text, nullable=True) user_agent = Column(String(255), nullable=True) secret_id = Column(String(100), ForeignKey('secrets.secret_id', ondelete='SET NULL'), nullable=True) user_id = Column(Integer, ForeignKey('users.id', ondelete='SET NULL'), nullable=True) user = relationship('User', back_populates='security_logs') secret = relationship('Secret', back_populates='security_logs') def to_dict(self): return { 'id': self.id, 'timestamp': self.timestamp.isoformat() if self.timestamp else None, 'ip': self.ip, 'event_type': self.event_type, 'details': self.details, 'user_agent': self.user_agent, 'secret_id': self.secret_id, 'user_id': self.user_id } def hash_password(password: str) -> str: password_bytes = password.encode('utf-8')[:72] salt = bcrypt.gensalt() return bcrypt.hashpw(password_bytes, salt).decode('utf-8') def verify_password(password: str, password_hash: str) -> bool: password_bytes = password.encode('utf-8')[:72] return bcrypt.checkpw(password_bytes, password_hash.encode('utf-8')) def create_user(username: str, password: str, role: str = 'user') -> bool: try: existing = User.query.filter_by(username=username).first() if existing: return False user = User(username=username, role=role) user.set_password(password) db.session.add(user) db.session.commit() return True except Exception as e: db.session.rollback() print(f"Ошибка создания пользователя: {e}") return False def get_user(username: str) -> User | None: return User.query.filter_by(username=username).first() def get_user_by_id(user_id: int) -> User | None: return User.query.get(user_id) def update_last_login(username: str): user = get_user(username) if user: user.last_login = datetime.now() db.session.commit() def get_all_users() -> list: return User.query.order_by(User.created_at.desc()).all() def delete_user(user_id: int): user = get_user_by_id(user_id) if user: db.session.delete(user) db.session.commit() def set_user_active(user_id: int, is_active: bool): user = get_user_by_id(user_id) if user: user.is_active = is_active db.session.commit() def set_user_role(user_id: int, role: str): user = get_user_by_id(user_id) if user: user.role = role db.session.commit() def log_creation(secret_id: str, client_info: dict, user_id: int = None, request_note: str = None, user_note: str = None): secret = Secret( secret_id=secret_id, user_id=user_id, created_ip=client_info.get('ip'), created_user_agent=client_info.get('user_agent'), request_note=request_note ) db.session.add(secret) db.session.commit() def log_view(secret_id: str, client_info: dict): secret = Secret.query.filter_by(secret_id=secret_id).first() if secret: if secret.created_at: view_duration = int((datetime.now() - secret.created_at).total_seconds()) else: view_duration = None secret.viewed_at = datetime.now() secret.viewed_ip = client_info.get('ip') secret.viewed_user_agent = client_info.get('user_agent') secret.is_viewed = True secret.view_duration = view_duration db.session.commit() def log_security_event(ip: str, event_type: str, details: str = None, user_agent: str = None, secret_id: str = None, user_id: int = None): try: ipaddress.ip_address(ip) except ValueError: ip = '0.0.0.0' if details and len(details) > 1000: details = details[:1000] if user_agent: user_agent = user_agent[:255] log = SecurityLog( ip=ip[:45], event_type=event_type[:50], details=details, user_agent=user_agent, secret_id=secret_id[:100] if secret_id else None, user_id=user_id ) try: db.session.add(log) db.session.commit() except Exception as e: db.session.rollback() print(f"Error logging security event: {e}") def get_secret_log(secret_id: str) -> dict | None: secret = Secret.query.filter_by(secret_id=secret_id).first() return secret.to_dict() if secret else None def get_all_logs(limit: int = 100, offset: int = 0, filter_viewed: str = None) -> list: if limit < 1: limit = 1 if limit > 1000: limit = 1000 if offset < 0: offset = 0 if offset > 100000: offset = 100000 if filter_viewed not in ['viewed', 'pending', None]: filter_viewed = None query = Secret.query if filter_viewed == 'viewed': query = query.filter_by(is_viewed=True) elif filter_viewed == 'pending': query = query.filter_by(is_viewed=False) return query.order_by(Secret.created_at.desc()).limit(limit).offset(offset).all() def get_stats() -> dict: total = Secret.query.count() viewed = Secret.query.filter_by(is_viewed=True).count() not_viewed = Secret.query.filter_by(is_viewed=False).count() unique_creators = db.session.query(Secret.created_ip).distinct().count() unique_viewers = db.session.query(Secret.viewed_ip).distinct().filter(Secret.viewed_ip.isnot(None)).count() from sqlalchemy import func avg_duration = db.session.query(func.avg(Secret.view_duration)).filter(Secret.is_viewed == True).scalar() security_events = SecurityLog.query.count() blocked_ips = SecurityLog.query.filter( SecurityLog.event_type.in_(['temporary_block', 'permanent_block']) ).with_entities(SecurityLog.ip).distinct().count() return { 'total': total, 'viewed': viewed, 'not_viewed': not_viewed, 'viewed_percent': round((viewed / total * 100) if total > 0 else 0, 2), 'unique_creators': unique_creators, 'unique_viewers': unique_viewers, 'avg_view_duration': round(avg_duration / 60, 2) if avg_duration else 0, 'security_events': security_events, 'blocked_ips': blocked_ips } def get_security_logs(limit: int = 50) -> list: return SecurityLog.query.order_by(SecurityLog.timestamp.desc()).limit(limit).all() def cleanup_old_data(): cutoff_date = datetime.now() - timedelta(days=30) old_secrets = Secret.query.filter(Secret.created_at < cutoff_date).all() for secret in old_secrets: db.session.delete(secret) cutoff_logs = datetime.now() - timedelta(days=90) old_logs = SecurityLog.query.filter(SecurityLog.timestamp < cutoff_logs).all() for log in old_logs: db.session.delete(log) db.session.commit() def get_user_secrets(user_id: int, limit: int = 100) -> list: if limit < 1: limit = 1 if limit > 500: limit = 500 secrets = Secret.query.filter_by(user_id=user_id).order_by(Secret.created_at.desc()).limit(limit).all() for secret in secrets: log = SecurityLog.query.filter_by( secret_id=secret.secret_id, event_type='secret_submitted_from_request' ).first() secret.is_request = bool(log) return secrets def get_requests_for_user(user_id: int) -> list: return SecurityLog.query.filter_by( user_id=user_id, event_type='secret_request_created' ).order_by(SecurityLog.timestamp.desc()).all() def init_db(): db.create_all()