|
|
@@ -0,0 +1,731 @@
|
|
|
+import tkinter as tk
|
|
|
+from tkinter import messagebox, scrolledtext, ttk
|
|
|
+import string
|
|
|
+import random
|
|
|
+import sqlite3
|
|
|
+from datetime import datetime
|
|
|
+import json
|
|
|
+import os
|
|
|
+
|
|
|
+
|
|
|
+class PasswordGenerator:
|
|
|
+ def __init__(self):
|
|
|
+ self.root = tk.Tk()
|
|
|
+ self.root.title("Генератор надежных паролей PRO")
|
|
|
+ self.root.geometry("750x700")
|
|
|
+
|
|
|
+ # Инициализация базы данных ДО создания интерфейса
|
|
|
+ self.init_database()
|
|
|
+
|
|
|
+ self.setup_ui()
|
|
|
+
|
|
|
+ # Переменные для настроек
|
|
|
+ self.current_password = ""
|
|
|
+ self.history_limit = 15
|
|
|
+ self.config_file = "config.json"
|
|
|
+ self.load_config()
|
|
|
+
|
|
|
+ def setup_ui(self):
|
|
|
+ # Создание стилей
|
|
|
+ style = ttk.Style()
|
|
|
+ style.configure('Custom.TButton', font=('Arial', 10, 'bold'))
|
|
|
+ style.configure('Title.TLabel', font=('Arial', 16, 'bold'))
|
|
|
+
|
|
|
+ # Главный контейнер
|
|
|
+ main_frame = ttk.Frame(self.root, padding="20")
|
|
|
+ main_frame.pack(fill=tk.BOTH, expand=True)
|
|
|
+
|
|
|
+ # Заголовок
|
|
|
+ title_label = ttk.Label(main_frame, text="🔐 ГЕНЕРАТОР НАДЕЖНЫХ ПАРОЛЕЙ",
|
|
|
+ style='Title.TLabel')
|
|
|
+ title_label.pack(pady=(0, 20))
|
|
|
+
|
|
|
+ # Фрейм настроек
|
|
|
+ settings_frame = ttk.LabelFrame(main_frame, text="Настройки генерации", padding="15")
|
|
|
+ settings_frame.pack(fill=tk.X, pady=(0, 15))
|
|
|
+
|
|
|
+ # Длина пароля
|
|
|
+ length_frame = ttk.Frame(settings_frame)
|
|
|
+ length_frame.pack(fill=tk.X, pady=5)
|
|
|
+
|
|
|
+ self.length_label = ttk.Label(length_frame, text="Длина пароля:", width=15)
|
|
|
+ self.length_label.pack(side=tk.LEFT)
|
|
|
+
|
|
|
+ self.length_var = tk.IntVar(value=16)
|
|
|
+ self.length_slider = ttk.Scale(length_frame, from_=8, to=32,
|
|
|
+ variable=self.length_var, orient=tk.HORIZONTAL)
|
|
|
+ self.length_slider.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=10)
|
|
|
+
|
|
|
+ self.length_display = ttk.Label(length_frame, textvariable=self.length_var, width=3)
|
|
|
+ self.length_display.pack(side=tk.LEFT)
|
|
|
+
|
|
|
+ # Чекбоксы для символов
|
|
|
+ self.letters_var = tk.BooleanVar(value=True)
|
|
|
+ self.digits_var = tk.BooleanVar(value=True)
|
|
|
+ self.punctuation_var = tk.BooleanVar(value=True)
|
|
|
+ self.exclude_similar = tk.BooleanVar(value=True)
|
|
|
+ self.exclude_ambiguous = tk.BooleanVar(value=False)
|
|
|
+
|
|
|
+ checks_frame = ttk.Frame(settings_frame)
|
|
|
+ checks_frame.pack(fill=tk.X, pady=10)
|
|
|
+
|
|
|
+ ttk.Checkbutton(checks_frame, text="Буквы (A-Z, a-z)",
|
|
|
+ variable=self.letters_var).pack(side=tk.LEFT, padx=10)
|
|
|
+ ttk.Checkbutton(checks_frame, text="Цифры (0-9)",
|
|
|
+ variable=self.digits_var).pack(side=tk.LEFT, padx=10)
|
|
|
+ ttk.Checkbutton(checks_frame, text="Спецсимволы (!@#$%)",
|
|
|
+ variable=self.punctuation_var).pack(side=tk.LEFT, padx=10)
|
|
|
+
|
|
|
+ advanced_frame = ttk.Frame(settings_frame)
|
|
|
+ advanced_frame.pack(fill=tk.X, pady=5)
|
|
|
+
|
|
|
+ ttk.Checkbutton(advanced_frame, text="Исключить похожие символы (i, l, 1, L, o, 0, O)",
|
|
|
+ variable=self.exclude_similar).pack(side=tk.LEFT, padx=10)
|
|
|
+ ttk.Checkbutton(advanced_frame, text="Исключить неоднозначные символы ({[]}<>\\|)",
|
|
|
+ variable=self.exclude_ambiguous).pack(side=tk.LEFT, padx=10)
|
|
|
+
|
|
|
+ # Кнопка генерации
|
|
|
+ self.generate_btn = ttk.Button(main_frame, text="⚡ СГЕНЕРИРОВАТЬ ПАРОЛЬ",
|
|
|
+ command=self.generate_password,
|
|
|
+ style='Custom.TButton')
|
|
|
+ self.generate_btn.pack(pady=10)
|
|
|
+
|
|
|
+ # Отображение пароля
|
|
|
+ result_frame = ttk.LabelFrame(main_frame, text="Сгенерированный пароль", padding="15")
|
|
|
+ result_frame.pack(fill=tk.X, pady=(0, 15))
|
|
|
+
|
|
|
+ self.password_var = tk.StringVar()
|
|
|
+ self.password_entry = ttk.Entry(result_frame, textvariable=self.password_var,
|
|
|
+ font=('Courier', 14), justify='center')
|
|
|
+ self.password_entry.pack(fill=tk.X)
|
|
|
+
|
|
|
+ # Кнопки действий с паролем
|
|
|
+ action_frame = ttk.Frame(main_frame)
|
|
|
+ action_frame.pack(pady=(0, 15))
|
|
|
+
|
|
|
+ ttk.Button(action_frame, text="📋 Копировать",
|
|
|
+ command=self.copy_to_clipboard).pack(side=tk.LEFT, padx=5)
|
|
|
+ ttk.Button(action_frame, text="💾 Сохранить",
|
|
|
+ command=self.save_password).pack(side=tk.LEFT, padx=5)
|
|
|
+ ttk.Button(action_frame, text="🔄 Новый",
|
|
|
+ command=self.generate_password).pack(side=tk.LEFT, padx=5)
|
|
|
+ ttk.Button(action_frame, text="🔍 Проверить надежность",
|
|
|
+ command=self.check_strength).pack(side=tk.LEFT, padx=5)
|
|
|
+
|
|
|
+ # История паролей
|
|
|
+ history_frame = ttk.LabelFrame(main_frame, text="История паролей", padding="15")
|
|
|
+ history_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 15))
|
|
|
+
|
|
|
+ # Панель инструментов истории
|
|
|
+ history_toolbar = ttk.Frame(history_frame)
|
|
|
+ history_toolbar.pack(fill=tk.X, pady=(0, 10))
|
|
|
+
|
|
|
+ ttk.Button(history_toolbar, text="🔄 Обновить",
|
|
|
+ command=self.load_history).pack(side=tk.LEFT, padx=2)
|
|
|
+ ttk.Button(history_toolbar, text="🗑️ Очистить",
|
|
|
+ command=self.clear_history_confirm).pack(side=tk.LEFT, padx=2)
|
|
|
+ ttk.Button(history_toolbar, text="📤 Экспорт",
|
|
|
+ command=self.export_history).pack(side=tk.LEFT, padx=2)
|
|
|
+ ttk.Button(history_toolbar, text="📊 Статистика",
|
|
|
+ command=self.show_statistics).pack(side=tk.LEFT, padx=2)
|
|
|
+
|
|
|
+ # Таблица истории
|
|
|
+ columns = ('id', 'password', 'strength', 'timestamp')
|
|
|
+ self.history_tree = ttk.Treeview(history_frame, columns=columns, show='headings', height=8)
|
|
|
+
|
|
|
+ self.history_tree.heading('id', text='ID')
|
|
|
+ self.history_tree.heading('password', text='Пароль')
|
|
|
+ self.history_tree.heading('strength', text='Надежность')
|
|
|
+ self.history_tree.heading('timestamp', text='Время создания')
|
|
|
+
|
|
|
+ self.history_tree.column('id', width=50, anchor='center')
|
|
|
+ self.history_tree.column('password', width=200)
|
|
|
+ self.history_tree.column('strength', width=100, anchor='center')
|
|
|
+ self.history_tree.column('timestamp', width=150)
|
|
|
+
|
|
|
+ scrollbar = ttk.Scrollbar(history_frame, orient=tk.VERTICAL, command=self.history_tree.yview)
|
|
|
+ self.history_tree.configure(yscrollcommand=scrollbar.set)
|
|
|
+
|
|
|
+ self.history_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
|
|
+ scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
|
|
+
|
|
|
+ # Привязка событий
|
|
|
+ self.history_tree.bind('<Double-Button-1>', self.on_history_select)
|
|
|
+
|
|
|
+ # Статус бар
|
|
|
+ self.status_bar = ttk.Label(main_frame, text="Готов к работе", relief=tk.SUNKEN)
|
|
|
+ self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
|
|
|
+
|
|
|
+ # Меню
|
|
|
+ self.create_menu()
|
|
|
+
|
|
|
+ def create_menu(self):
|
|
|
+ menubar = tk.Menu(self.root)
|
|
|
+ self.root.config(menu=menubar)
|
|
|
+
|
|
|
+ # Меню Файл
|
|
|
+ file_menu = tk.Menu(menubar, tearoff=0)
|
|
|
+ menubar.add_cascade(label="Файл", menu=file_menu)
|
|
|
+ file_menu.add_command(label="Сохранить настройки", command=self.save_config)
|
|
|
+ file_menu.add_command(label="Загрузить настройки", command=self.load_config)
|
|
|
+ file_menu.add_separator()
|
|
|
+ file_menu.add_command(label="Экспорт всех паролей", command=self.export_all_passwords)
|
|
|
+ file_menu.add_separator()
|
|
|
+ file_menu.add_command(label="Выход", command=self.root.quit)
|
|
|
+
|
|
|
+ # Меню Настройки
|
|
|
+ settings_menu = tk.Menu(menubar, tearoff=0)
|
|
|
+ menubar.add_cascade(label="Настройки", menu=settings_menu)
|
|
|
+ settings_menu.add_command(label="Ограничение истории",
|
|
|
+ command=self.set_history_limit)
|
|
|
+ settings_menu.add_command(label="Сбросить настройки",
|
|
|
+ command=self.reset_settings)
|
|
|
+
|
|
|
+ # Меню Справка
|
|
|
+ help_menu = tk.Menu(menubar, tearoff=0)
|
|
|
+ menubar.add_cascade(label="Справка", menu=help_menu)
|
|
|
+ help_menu.add_command(label="О программе", command=self.show_about)
|
|
|
+ help_menu.add_command(label="Рекомендации", command=self.show_tips)
|
|
|
+
|
|
|
+ def init_database(self):
|
|
|
+ try:
|
|
|
+ conn = sqlite3.connect('passwords.db')
|
|
|
+ cursor = conn.cursor()
|
|
|
+
|
|
|
+ # Проверяем существование таблицы
|
|
|
+ cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='passwords'")
|
|
|
+ table_exists = cursor.fetchone()
|
|
|
+
|
|
|
+ if table_exists:
|
|
|
+ # Проверяем структуру существующей таблицы
|
|
|
+ cursor.execute("PRAGMA table_info(passwords)")
|
|
|
+ columns = cursor.fetchall()
|
|
|
+ column_names = [col[1] for col in columns]
|
|
|
+
|
|
|
+ # Если таблица создавалась по старому коду, пересоздаем её
|
|
|
+ if 'generated_password' in column_names:
|
|
|
+ # Сохраняем старые данные
|
|
|
+ cursor.execute("SELECT generated_password, timestamp FROM passwords")
|
|
|
+ old_data = cursor.fetchall()
|
|
|
+
|
|
|
+ # Удаляем старую таблицу
|
|
|
+ cursor.execute("DROP TABLE passwords")
|
|
|
+
|
|
|
+ # Создаем новую таблицу
|
|
|
+ cursor.execute('''
|
|
|
+ CREATE TABLE passwords (
|
|
|
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
+ password TEXT NOT NULL,
|
|
|
+ strength TEXT,
|
|
|
+ timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
|
+ )
|
|
|
+ ''')
|
|
|
+
|
|
|
+ # Восстанавливаем данные
|
|
|
+ for data in old_data:
|
|
|
+ password = data[0]
|
|
|
+ timestamp = data[1]
|
|
|
+ strength = self.calculate_strength(password)
|
|
|
+ cursor.execute('''
|
|
|
+ INSERT INTO passwords (password, strength, timestamp)
|
|
|
+ VALUES (?, ?, ?)
|
|
|
+ ''', (password, strength, timestamp))
|
|
|
+ elif 'password' not in column_names:
|
|
|
+ # Таблица существует, но нет колонки password - добавляем
|
|
|
+ cursor.execute('''
|
|
|
+ ALTER TABLE passwords ADD COLUMN password TEXT
|
|
|
+ ''')
|
|
|
+ else:
|
|
|
+ # Создаем новую таблицу
|
|
|
+ cursor.execute('''
|
|
|
+ CREATE TABLE passwords (
|
|
|
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
+ password TEXT NOT NULL,
|
|
|
+ strength TEXT,
|
|
|
+ timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
|
+ )
|
|
|
+ ''')
|
|
|
+
|
|
|
+ conn.commit()
|
|
|
+ conn.close()
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ messagebox.showerror("Ошибка", f"Ошибка инициализации БД: {str(e)}")
|
|
|
+
|
|
|
+ def generate_password(self):
|
|
|
+ try:
|
|
|
+ length = self.length_var.get()
|
|
|
+ selected_chars = ''
|
|
|
+
|
|
|
+ if self.letters_var.get():
|
|
|
+ letters = string.ascii_letters
|
|
|
+ if self.exclude_similar.get():
|
|
|
+ letters = letters.replace('i', '').replace('l', '').replace('I', '').replace('L', '')
|
|
|
+ letters = letters.replace('o', '').replace('O', '')
|
|
|
+ selected_chars += letters
|
|
|
+
|
|
|
+ if self.digits_var.get():
|
|
|
+ digits = string.digits
|
|
|
+ if self.exclude_similar.get():
|
|
|
+ digits = digits.replace('0', '').replace('1', '')
|
|
|
+ selected_chars += digits
|
|
|
+
|
|
|
+ if self.punctuation_var.get():
|
|
|
+ punctuation = string.punctuation
|
|
|
+ if self.exclude_ambiguous.get():
|
|
|
+ ambiguous = '{}[]()<>\\|`~'
|
|
|
+ for char in ambiguous:
|
|
|
+ punctuation = punctuation.replace(char, '')
|
|
|
+ selected_chars += punctuation
|
|
|
+
|
|
|
+ if not selected_chars:
|
|
|
+ messagebox.showwarning("Предупреждение",
|
|
|
+ "Выберите хотя бы один тип символов!")
|
|
|
+ return
|
|
|
+
|
|
|
+ # Гарантируем хотя бы по одному символу каждого выбранного типа
|
|
|
+ password_chars = []
|
|
|
+ if self.letters_var.get():
|
|
|
+ letters = string.ascii_letters
|
|
|
+ if self.exclude_similar.get():
|
|
|
+ letters = letters.replace('i', '').replace('l', '').replace('I', '').replace('L', '')
|
|
|
+ letters = letters.replace('o', '').replace('O', '')
|
|
|
+ password_chars.append(random.choice(letters))
|
|
|
+ length -= 1
|
|
|
+
|
|
|
+ if self.digits_var.get():
|
|
|
+ digits = string.digits
|
|
|
+ if self.exclude_similar.get():
|
|
|
+ digits = digits.replace('0', '').replace('1', '')
|
|
|
+ password_chars.append(random.choice(digits))
|
|
|
+ length -= 1
|
|
|
+
|
|
|
+ if self.punctuation_var.get():
|
|
|
+ punctuation = string.punctuation
|
|
|
+ if self.exclude_ambiguous.get():
|
|
|
+ ambiguous = '{}[]()<>\\|`~'
|
|
|
+ for char in ambiguous:
|
|
|
+ punctuation = punctuation.replace(char, '')
|
|
|
+ password_chars.append(random.choice(punctuation))
|
|
|
+ length -= 1
|
|
|
+
|
|
|
+ # Добавляем остальные случайные символы
|
|
|
+ for _ in range(max(0, length)):
|
|
|
+ password_chars.append(random.choice(selected_chars))
|
|
|
+
|
|
|
+ # Перемешиваем символы
|
|
|
+ random.shuffle(password_chars)
|
|
|
+ password = ''.join(password_chars)
|
|
|
+
|
|
|
+ self.current_password = password
|
|
|
+ self.password_var.set(password)
|
|
|
+
|
|
|
+ # Определяем цвет в зависимости от надежности
|
|
|
+ strength = self.calculate_strength(password)
|
|
|
+ color = self.get_strength_color(strength)
|
|
|
+ self.password_entry.config(foreground=color)
|
|
|
+
|
|
|
+ self.status_bar.config(text=f"Пароль сгенерирован | Надежность: {strength}")
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ messagebox.showerror("Ошибка", f"Произошла ошибка: {str(e)}")
|
|
|
+
|
|
|
+ def calculate_strength(self, password):
|
|
|
+ score = 0
|
|
|
+
|
|
|
+ # Длина
|
|
|
+ if len(password) >= 20:
|
|
|
+ score += 3
|
|
|
+ elif len(password) >= 16:
|
|
|
+ score += 2
|
|
|
+ elif len(password) >= 12:
|
|
|
+ score += 1
|
|
|
+
|
|
|
+ # Разнообразие символов
|
|
|
+ has_upper = any(c.isupper() for c in password)
|
|
|
+ has_lower = any(c.islower() for c in password)
|
|
|
+ has_digit = any(c.isdigit() for c in password)
|
|
|
+ has_special = any(c in string.punctuation for c in password)
|
|
|
+
|
|
|
+ types_count = sum([has_upper, has_lower, has_digit, has_special])
|
|
|
+ score += types_count
|
|
|
+
|
|
|
+ # Сложность
|
|
|
+ if len(set(password)) / len(password) > 0.7:
|
|
|
+ score += 1
|
|
|
+
|
|
|
+ if score >= 6:
|
|
|
+ return "Очень надежный"
|
|
|
+ elif score >= 4:
|
|
|
+ return "Надежный"
|
|
|
+ elif score >= 2:
|
|
|
+ return "Средний"
|
|
|
+ else:
|
|
|
+ return "Слабый"
|
|
|
+
|
|
|
+ def get_strength_color(self, strength):
|
|
|
+ colors = {
|
|
|
+ "Очень надежный": "green",
|
|
|
+ "Надежный": "blue",
|
|
|
+ "Средний": "orange",
|
|
|
+ "Слабый": "red"
|
|
|
+ }
|
|
|
+ return colors.get(strength, "black")
|
|
|
+
|
|
|
+ def copy_to_clipboard(self):
|
|
|
+ if self.current_password:
|
|
|
+ self.root.clipboard_clear()
|
|
|
+ self.root.clipboard_append(self.current_password)
|
|
|
+ self.status_bar.config(text="Пароль скопирован в буфер обмена")
|
|
|
+ messagebox.showinfo("Успех", "Пароль скопирован в буфер обмена!")
|
|
|
+ else:
|
|
|
+ messagebox.showwarning("Предупреждение", "Сначала сгенерируйте пароль!")
|
|
|
+
|
|
|
+ def save_password(self):
|
|
|
+ if not self.current_password:
|
|
|
+ messagebox.showwarning("Предупреждение", "Сначала сгенерируйте пароль!")
|
|
|
+ return
|
|
|
+
|
|
|
+ try:
|
|
|
+ conn = sqlite3.connect('passwords.db')
|
|
|
+ cursor = conn.cursor()
|
|
|
+
|
|
|
+ strength = self.calculate_strength(self.current_password)
|
|
|
+
|
|
|
+ cursor.execute('''
|
|
|
+ INSERT INTO passwords (password, strength, timestamp)
|
|
|
+ VALUES (?, ?, ?)
|
|
|
+ ''', (self.current_password, strength, datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
|
|
|
+
|
|
|
+ conn.commit()
|
|
|
+ conn.close()
|
|
|
+
|
|
|
+ self.load_history()
|
|
|
+ self.status_bar.config(text="Пароль сохранен в базу данных")
|
|
|
+ messagebox.showinfo("Успех", "Пароль успешно сохранен!")
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ messagebox.showerror("Ошибка", f"Не удалось сохранить пароль: {str(e)}")
|
|
|
+
|
|
|
+ def load_history(self):
|
|
|
+ try:
|
|
|
+ # Очищаем дерево
|
|
|
+ for item in self.history_tree.get_children():
|
|
|
+ self.history_tree.delete(item)
|
|
|
+
|
|
|
+ conn = sqlite3.connect('passwords.db')
|
|
|
+ cursor = conn.cursor()
|
|
|
+
|
|
|
+ cursor.execute('''
|
|
|
+ SELECT id, password, strength, timestamp
|
|
|
+ FROM passwords
|
|
|
+ ORDER BY id DESC
|
|
|
+ LIMIT ?
|
|
|
+ ''', (self.history_limit,))
|
|
|
+
|
|
|
+ records = cursor.fetchall()
|
|
|
+
|
|
|
+ for record in records:
|
|
|
+ # Маскируем пароль для отображения
|
|
|
+ masked_password = self.mask_password(record[1])
|
|
|
+ self.history_tree.insert('', 'end', values=(
|
|
|
+ record[0], masked_password, record[2], record[3]
|
|
|
+ ))
|
|
|
+
|
|
|
+ conn.close()
|
|
|
+
|
|
|
+ self.status_bar.config(text=f"Загружено {len(records)} записей")
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ messagebox.showerror("Ошибка", f"Не удалось загрузить историю: {str(e)}")
|
|
|
+
|
|
|
+ def mask_password(self, password):
|
|
|
+ if not password:
|
|
|
+ return "****"
|
|
|
+ if len(password) <= 4:
|
|
|
+ return "****"
|
|
|
+ return password[:2] + "*" * (len(password) - 4) + password[-2:]
|
|
|
+
|
|
|
+ def on_history_select(self, event):
|
|
|
+ selection = self.history_tree.selection()
|
|
|
+ if selection:
|
|
|
+ item = self.history_tree.item(selection[0])
|
|
|
+ item_id = item['values'][0]
|
|
|
+
|
|
|
+ conn = sqlite3.connect('passwords.db')
|
|
|
+ cursor = conn.cursor()
|
|
|
+ cursor.execute('SELECT password FROM passwords WHERE id = ?', (item_id,))
|
|
|
+ result = cursor.fetchone()
|
|
|
+ conn.close()
|
|
|
+
|
|
|
+ if result:
|
|
|
+ password = result[0]
|
|
|
+ self.current_password = password
|
|
|
+ self.password_var.set(password)
|
|
|
+ self.status_bar.config(text=f"Выбран пароль ID: {item_id}")
|
|
|
+
|
|
|
+ def clear_history_confirm(self):
|
|
|
+ if messagebox.askyesno("Подтверждение",
|
|
|
+ "Вы уверены, что хотите очистить всю историю паролей?"):
|
|
|
+ self.clear_history()
|
|
|
+
|
|
|
+ def clear_history(self):
|
|
|
+ try:
|
|
|
+ conn = sqlite3.connect('passwords.db')
|
|
|
+ cursor = conn.cursor()
|
|
|
+ cursor.execute("DELETE FROM passwords")
|
|
|
+ conn.commit()
|
|
|
+ conn.close()
|
|
|
+
|
|
|
+ self.load_history()
|
|
|
+ messagebox.showinfo("Успех", "История паролей очищена!")
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ messagebox.showerror("Ошибка", f"Не удалось очистить историю: {str(e)}")
|
|
|
+
|
|
|
+ def check_strength(self):
|
|
|
+ if not self.current_password:
|
|
|
+ messagebox.showwarning("Предупреждение", "Сначала сгенерируйте пароль!")
|
|
|
+ return
|
|
|
+
|
|
|
+ strength = self.calculate_strength(self.current_password)
|
|
|
+ color = self.get_strength_color(strength)
|
|
|
+
|
|
|
+ analysis = self.analyze_password(self.current_password)
|
|
|
+
|
|
|
+ messagebox.showinfo("Анализ пароля",
|
|
|
+ f"Надежность: {strength}\n\n"
|
|
|
+ f"Длина: {len(self.current_password)} символов\n"
|
|
|
+ f"Заглавные буквы: {'Есть' if any(c.isupper() for c in self.current_password) else 'Нет'}\n"
|
|
|
+ f"Строчные буквы: {'Есть' if any(c.islower() for c in self.current_password) else 'Нет'}\n"
|
|
|
+ f"Цифры: {'Есть' if any(c.isdigit() for c in self.current_password) else 'Нет'}\n"
|
|
|
+ f"Спецсимволы: {'Есть' if any(c in string.punctuation for c in self.current_password) else 'Нет'}\n\n"
|
|
|
+ f"{analysis}")
|
|
|
+
|
|
|
+ def analyze_password(self, password):
|
|
|
+ suggestions = []
|
|
|
+
|
|
|
+ if len(password) < 12:
|
|
|
+ suggestions.append("• Увеличьте длину пароля (рекомендуется 12+ символов)")
|
|
|
+
|
|
|
+ if not any(c.isupper() for c in password):
|
|
|
+ suggestions.append("• Добавьте заглавные буквы")
|
|
|
+
|
|
|
+ if not any(c.islower() for c in password):
|
|
|
+ suggestions.append("• Добавьте строчные буквы")
|
|
|
+
|
|
|
+ if not any(c.isdigit() for c in password):
|
|
|
+ suggestions.append("• Добавьте цифры")
|
|
|
+
|
|
|
+ if not any(c in string.punctuation for c in password):
|
|
|
+ suggestions.append("• Добавьте специальные символы")
|
|
|
+
|
|
|
+ if len(set(password)) / len(password) < 0.6:
|
|
|
+ suggestions.append("• Используйте больше уникальных символов")
|
|
|
+
|
|
|
+ if suggestions:
|
|
|
+ return "Рекомендации:\n" + "\n".join(suggestions)
|
|
|
+ else:
|
|
|
+ return "Пароль соответствует всем рекомендациям безопасности!"
|
|
|
+
|
|
|
+ def export_history(self):
|
|
|
+ try:
|
|
|
+ conn = sqlite3.connect('passwords.db')
|
|
|
+ cursor = conn.cursor()
|
|
|
+ cursor.execute('SELECT * FROM passwords ORDER BY id DESC')
|
|
|
+ records = cursor.fetchall()
|
|
|
+ conn.close()
|
|
|
+
|
|
|
+ if not records:
|
|
|
+ messagebox.showwarning("Предупреждение", "Нет данных для экспорта!")
|
|
|
+ return
|
|
|
+
|
|
|
+ filename = f"passwords_export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
|
|
|
+
|
|
|
+ with open(filename, 'w', encoding='utf-8') as f:
|
|
|
+ f.write("Экспорт паролей\n")
|
|
|
+ f.write("=" * 50 + "\n\n")
|
|
|
+
|
|
|
+ for record in records:
|
|
|
+ f.write(f"ID: {record[0]}\n")
|
|
|
+ f.write(f"Пароль: {record[1]}\n")
|
|
|
+ f.write(f"Надежность: {record[2]}\n")
|
|
|
+ f.write(f"Создан: {record[3]}\n")
|
|
|
+ f.write("-" * 30 + "\n")
|
|
|
+
|
|
|
+ self.status_bar.config(text=f"Данные экспортированы в файл: {filename}")
|
|
|
+ messagebox.showinfo("Успех", f"Данные успешно экспортированы в файл:\n{filename}")
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ messagebox.showerror("Ошибка", f"Ошибка экспорта: {str(e)}")
|
|
|
+
|
|
|
+ def export_all_passwords(self):
|
|
|
+ self.export_history()
|
|
|
+
|
|
|
+ def show_statistics(self):
|
|
|
+ try:
|
|
|
+ conn = sqlite3.connect('passwords.db')
|
|
|
+ cursor = conn.cursor()
|
|
|
+
|
|
|
+ cursor.execute("SELECT COUNT(*) FROM passwords")
|
|
|
+ total = cursor.fetchone()[0]
|
|
|
+
|
|
|
+ cursor.execute("SELECT COUNT(*) FROM passwords WHERE strength = 'Очень надежный'")
|
|
|
+ very_strong = cursor.fetchone()[0]
|
|
|
+
|
|
|
+ cursor.execute("SELECT COUNT(*) FROM passwords WHERE strength = 'Надежный'")
|
|
|
+ strong = cursor.fetchone()[0]
|
|
|
+
|
|
|
+ cursor.execute("SELECT COUNT(*) FROM passwords WHERE strength = 'Средний'")
|
|
|
+ medium = cursor.fetchone()[0]
|
|
|
+
|
|
|
+ cursor.execute("SELECT COUNT(*) FROM passwords WHERE strength = 'Слабый'")
|
|
|
+ weak = cursor.fetchone()[0]
|
|
|
+
|
|
|
+ cursor.execute("SELECT MIN(timestamp), MAX(timestamp) FROM passwords")
|
|
|
+ dates = cursor.fetchone()
|
|
|
+
|
|
|
+ conn.close()
|
|
|
+
|
|
|
+ stats_text = f"""
|
|
|
+ 📊 Статистика паролей
|
|
|
+
|
|
|
+ Всего паролей: {total}
|
|
|
+
|
|
|
+ По надежности:
|
|
|
+ • Очень надежные: {very_strong}
|
|
|
+ • Надежные: {strong}
|
|
|
+ • Средние: {medium}
|
|
|
+ • Слабые: {weak}
|
|
|
+
|
|
|
+ Временной диапазон:
|
|
|
+ • Первый пароль: {dates[0] if dates[0] else 'Нет данных'}
|
|
|
+ • Последний пароль: {dates[1] if dates[1] else 'Нет данных'}
|
|
|
+ """
|
|
|
+
|
|
|
+ messagebox.showinfo("Статистика", stats_text)
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ messagebox.showerror("Ошибка", f"Не удалось получить статистику: {str(e)}")
|
|
|
+
|
|
|
+ def set_history_limit(self):
|
|
|
+ dialog = tk.Toplevel(self.root)
|
|
|
+ dialog.title("Ограничение истории")
|
|
|
+ dialog.geometry("300x150")
|
|
|
+
|
|
|
+ tk.Label(dialog, text="Максимальное количество записей:").pack(pady=10)
|
|
|
+
|
|
|
+ limit_var = tk.IntVar(value=self.history_limit)
|
|
|
+ spinbox = tk.Spinbox(dialog, from_=5, to=100, textvariable=limit_var, width=10)
|
|
|
+ spinbox.pack(pady=10)
|
|
|
+
|
|
|
+ def apply_limit():
|
|
|
+ self.history_limit = limit_var.get()
|
|
|
+ self.load_history()
|
|
|
+ dialog.destroy()
|
|
|
+ messagebox.showinfo("Успех", f"Лимит истории установлен: {self.history_limit}")
|
|
|
+
|
|
|
+ tk.Button(dialog, text="Применить", command=apply_limit).pack(pady=10)
|
|
|
+
|
|
|
+ def save_config(self):
|
|
|
+ config = {
|
|
|
+ 'length': self.length_var.get(),
|
|
|
+ 'letters': self.letters_var.get(),
|
|
|
+ 'digits': self.digits_var.get(),
|
|
|
+ 'punctuation': self.punctuation_var.get(),
|
|
|
+ 'exclude_similar': self.exclude_similar.get(),
|
|
|
+ 'exclude_ambiguous': self.exclude_ambiguous.get(),
|
|
|
+ 'history_limit': self.history_limit
|
|
|
+ }
|
|
|
+
|
|
|
+ try:
|
|
|
+ with open(self.config_file, 'w') as f:
|
|
|
+ json.dump(config, f)
|
|
|
+
|
|
|
+ self.status_bar.config(text="Настройки сохранены")
|
|
|
+ messagebox.showinfo("Успех", "Настройки успешно сохранены!")
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ messagebox.showerror("Ошибка", f"Не удалось сохранить настройки: {str(e)}")
|
|
|
+
|
|
|
+ def load_config(self):
|
|
|
+ if not os.path.exists(self.config_file):
|
|
|
+ return
|
|
|
+
|
|
|
+ try:
|
|
|
+ with open(self.config_file, 'r') as f:
|
|
|
+ config = json.load(f)
|
|
|
+
|
|
|
+ self.length_var.set(config.get('length', 16))
|
|
|
+ self.letters_var.set(config.get('letters', True))
|
|
|
+ self.digits_var.set(config.get('digits', True))
|
|
|
+ self.punctuation_var.set(config.get('punctuation', True))
|
|
|
+ self.exclude_similar.set(config.get('exclude_similar', True))
|
|
|
+ self.exclude_ambiguous.set(config.get('exclude_ambiguous', False))
|
|
|
+ self.history_limit = config.get('history_limit', 15)
|
|
|
+
|
|
|
+ self.status_bar.config(text="Настройки загружены")
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ messagebox.showerror("Ошибка", f"Не удалось загрузить настройки: {str(e)}")
|
|
|
+
|
|
|
+ def reset_settings(self):
|
|
|
+ if messagebox.askyesno("Подтверждение",
|
|
|
+ "Вы уверены, что хотите сбросить все настройки к значениям по умолчанию?"):
|
|
|
+ self.length_var.set(16)
|
|
|
+ self.letters_var.set(True)
|
|
|
+ self.digits_var.set(True)
|
|
|
+ self.punctuation_var.set(True)
|
|
|
+ self.exclude_similar.set(True)
|
|
|
+ self.exclude_ambiguous.set(False)
|
|
|
+ self.history_limit = 15
|
|
|
+
|
|
|
+ messagebox.showinfo("Успех", "Настройки сброшены к значениям по умолчанию!")
|
|
|
+
|
|
|
+ def show_about(self):
|
|
|
+ about_text = """
|
|
|
+ 🔐 Генератор надежных паролей PRO
|
|
|
+
|
|
|
+ Версия: 2.0
|
|
|
+ Разработчик: Password Security Team
|
|
|
+
|
|
|
+ Функции:
|
|
|
+ • Генерация паролей с настройками
|
|
|
+ • Проверка надежности паролей
|
|
|
+ • Сохранение истории паролей
|
|
|
+ • Экспорт данных
|
|
|
+ • Статистика использования
|
|
|
+
|
|
|
+ Используйте этот инструмент для создания
|
|
|
+ безопасных паролей для всех ваших аккаунтов!
|
|
|
+ """
|
|
|
+ messagebox.showinfo("О программе", about_text)
|
|
|
+
|
|
|
+ def show_tips(self):
|
|
|
+ tips = """
|
|
|
+ 💡 Советы по созданию надежных паролей:
|
|
|
+
|
|
|
+ 1. Используйте длину не менее 12 символов
|
|
|
+ 2. Сочетайте разные типы символов
|
|
|
+ 3. Избегайте личной информации
|
|
|
+ 4. Не используйте один пароль на всех сайтах
|
|
|
+ 5. Регулярно меняйте важные пароли
|
|
|
+ 6. Используйте менеджер паролей
|
|
|
+ 7. Включайте двухфакторную аутентификацию
|
|
|
+
|
|
|
+ Помните: Лучший пароль - тот, который вы не можете запомнить,
|
|
|
+ но ваш менеджер паролей может!
|
|
|
+ """
|
|
|
+ messagebox.showinfo("Рекомендации по безопасности", tips)
|
|
|
+
|
|
|
+ def run(self):
|
|
|
+ self.load_history()
|
|
|
+ self.root.mainloop()
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ app = PasswordGenerator()
|
|
|
+ app.run()
|
|
|
+
|
|
|
+
|
|
|
+
|