121 lines
5.4 KiB
Python
121 lines
5.4 KiB
Python
from flask_sqlalchemy import SQLAlchemy
|
|
from flask_login import UserMixin
|
|
from werkzeug.security import generate_password_hash, check_password_hash
|
|
from cryptography.fernet import Fernet
|
|
import os
|
|
import json
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# 1. Инициализируем расширение без привязки к app
|
|
from extensions import db
|
|
|
|
# 2. Инициализация Fernet вынесена в функцию, чтобы она вызывалась ПОСЛЕ загрузки .env
|
|
fernet = None
|
|
|
|
def init_encryption(app):
|
|
"""Инициализирует Fernet после того, как конфигурация загружена."""
|
|
global fernet
|
|
encryption_key_str = app.config.get('ENCRYPTION_KEY')
|
|
if not encryption_key_str:
|
|
logger.error("Переменная окружения ENCRYPTION_KEY не установлена! Шифрование паролей RMS не будет работать.")
|
|
logger.warning("Генерируется временный ключ шифрования. Для продакшена ОБЯЗАТЕЛЬНО установите ENCRYPTION_KEY!")
|
|
encryption_key = Fernet.generate_key()
|
|
else:
|
|
try:
|
|
encryption_key = encryption_key_str.encode('utf-8')
|
|
Fernet(encryption_key)
|
|
logger.info("Ключ шифрования ENCRYPTION_KEY успешно загружен.")
|
|
except Exception as e:
|
|
logger.critical(f"Недопустимый формат ключа ENCRYPTION_KEY: {e}")
|
|
raise ValueError("Недопустимый формат ключа ENCRYPTION_KEY.") from e
|
|
fernet = Fernet(encryption_key)
|
|
|
|
|
|
class User(db.Model, UserMixin):
|
|
# ... код класса User остается без изменений ...
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
username = db.Column(db.String(80), unique=True, nullable=False)
|
|
password_hash = db.Column(db.String(256))
|
|
config = db.relationship('UserConfig', backref='user', uselist=False, cascade="all, delete-orphan")
|
|
|
|
def set_password(self, password):
|
|
self.password_hash = generate_password_hash(password)
|
|
|
|
def check_password(self, password):
|
|
return check_password_hash(self.password_hash, password)
|
|
|
|
def __repr__(self):
|
|
return f'<User {self.username}>'
|
|
|
|
|
|
class UserConfig(db.Model):
|
|
# ... код класса UserConfig почти без изменений ...
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False, unique=True)
|
|
rms_host = db.Column(db.String(200))
|
|
rms_login = db.Column(db.String(100))
|
|
rms_password_encrypted = db.Column(db.LargeBinary)
|
|
google_cred_file_path = db.Column(db.String(300))
|
|
google_sheet_url = db.Column(db.String(300))
|
|
google_client_email = db.Column(db.String(200))
|
|
presets_json = db.Column(db.Text, default='[]')
|
|
sheets_json = db.Column(db.Text, default='[]')
|
|
mappings_json = db.Column(db.Text, default='{}')
|
|
|
|
@property
|
|
def rms_password(self):
|
|
"""Дешифрует пароль RMS при доступе."""
|
|
if not fernet:
|
|
raise RuntimeError("Fernet encryption is not initialized. Call init_encryption(app) first.")
|
|
if self.rms_password_encrypted:
|
|
try:
|
|
return fernet.decrypt(self.rms_password_encrypted).decode('utf-8')
|
|
except Exception as e:
|
|
logger.error(f"Ошибка дешифрования пароля для user_id {self.user_id}: {e}")
|
|
return None
|
|
return None
|
|
|
|
@rms_password.setter
|
|
def rms_password(self, value):
|
|
"""Шифрует пароль RMS при установке."""
|
|
if not fernet:
|
|
raise RuntimeError("Fernet encryption is not initialized. Call init_encryption(app) first.")
|
|
if value:
|
|
self.rms_password_encrypted = fernet.encrypt(value.encode('utf-8'))
|
|
else:
|
|
self.rms_password_encrypted = None
|
|
|
|
# ... остальные properties (presets, sheets, mappings) и методы (get_rms_dict, get_google_dict) остаются без изменений ...
|
|
@property
|
|
def presets(self):
|
|
return json.loads(self.presets_json or '[]')
|
|
@presets.setter
|
|
def presets(self, value):
|
|
self.presets_json = json.dumps(value or [], ensure_ascii=False)
|
|
@property
|
|
def sheets(self):
|
|
return json.loads(self.sheets_json or '[]')
|
|
@sheets.setter
|
|
def sheets(self, value):
|
|
self.sheets_json = json.dumps(value or [], ensure_ascii=False)
|
|
@property
|
|
def mappings(self):
|
|
return json.loads(self.mappings_json or '{}')
|
|
@mappings.setter
|
|
def mappings(self, value):
|
|
self.mappings_json = json.dumps(value or {}, ensure_ascii=False)
|
|
def get_rms_dict(self):
|
|
return {
|
|
'host': self.rms_host or '',
|
|
'login': self.rms_login or '',
|
|
'password_is_set': bool(self.rms_password_encrypted)
|
|
}
|
|
def get_google_dict(self):
|
|
return {
|
|
'cred_file_is_set': bool(self.google_cred_file_path and os.path.exists(self.google_cred_file_path)),
|
|
'sheet_url': self.google_sheet_url or ''
|
|
}
|
|
def __repr__(self):
|
|
return f'<UserConfig for User ID {self.user_id}>' |