import tkinter as tk from tkinter import ttk, messagebox import random import time try: import winsound SOUND_ENABLED = True except: SOUND_ENABLED = False class SecurityGame: def __init__(self, root): self.root = root self.root.title("Игра: Управление доступом и контроль печати") self.root.geometry("900x700") self.root.configure(bg="#2c3e50") # Стили self.style = ttk.Style() self.style.theme_use("clam") self.style.configure("TLabel", background="#2c3e50", foreground="white", font=("Arial", 11)) self.style.configure("TButton", font=("Arial", 11, "bold"), background="#3498db") self.style.configure("TFrame", background="#2c3e50") # Переменные self.current_scene = 0 self.scenes = [self.scene1, self.scene2, self.scene3, self.scene4, self.scene5] self.score = 0 # Заголовок и прогресс self.header_frame = tk.Frame(root, bg="#1a252f", height=80) self.header_frame.pack(fill=tk.X) self.title_label = tk.Label(self.header_frame, text="Управление доступом и контроль печати", font=("Arial", 18, "bold"), fg="#ecf0f1", bg="#1a252f") self.title_label.pack(pady=10) self.progress = ttk.Progressbar(self.header_frame, length=400, mode='determinate') self.progress.pack(pady=5) self.score_label = tk.Label(self.header_frame, text="Очки: 0", font=("Arial", 12), fg="#f1c40f", bg="#1a252f") self.score_label.pack() # Основной фрейм для сцен self.main_frame = tk.Frame(root, bg="#2c3e50") self.main_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20) # Фрейм для подсказок self.hint_frame = tk.Frame(root, bg="#34495e", height=50) self.hint_frame.pack(fill=tk.X, side=tk.BOTTOM) self.hint_label = tk.Label(self.hint_frame, text="Нажми 'Подсказка' для помощи", font=("Arial", 10), fg="#bdc3c7", bg="#34495e") self.hint_label.pack(pady=5) self.load_scene() def play_sound(self, success=True): if SOUND_ENABLED: if success: winsound.Beep(1000, 200) else: winsound.Beep(400, 500) def update_progress(self): value = (self.current_scene / len(self.scenes)) * 100 self.progress['value'] = value self.score_label.config(text=f"Очки: {self.score}") def show_hint(self, hint_text): self.hint_label.config(text=f"💡 Подсказка: {hint_text}", fg="#f1c40f") self.hint_frame.configure(bg="#2c3e50") self.root.after(5000, lambda: self.hint_label.config(text="Нажми 'Подсказка' для помощи", fg="#bdc3c7", bg="#34495e")) def clear_frame(self): for widget in self.main_frame.winfo_children(): widget.destroy() def animate_widget(self, widget, color_from, color_to, steps=10): """Простая анимация смены цвета фона виджета""" r1, g1, b1 = self.hex_to_rgb(color_from) r2, g2, b2 = self.hex_to_rgb(color_to) for i in range(steps+1): r = r1 + (r2 - r1) * i // steps g = g1 + (g2 - g1) * i // steps b = b1 + (b2 - b1) * i // steps color = f"#{r:02x}{g:02x}{b:02x}" widget.configure(bg=color) self.root.update() time.sleep(0.02) def hex_to_rgb(self, hex_color): hex_color = hex_color.lstrip('#') return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4)) def load_scene(self): self.clear_frame() self.update_progress() self.scenes[self.current_scene]() def next_scene(self): self.current_scene += 1 self.score += 10 self.update_progress() if self.current_scene < len(self.scenes): self.load_scene() else: messagebox.showinfo("Поздравляем!", f"Вы успешно завершили игру!\nФинальный счёт: {self.score} очков.\nТеперь вы эксперт по безопасности!") self.root.quit() # СЦЕНА 1: Выбор модели доступа с анимацией def scene1(self): tk.Label(self.main_frame, text="СЦЕНА 1: Модели управления доступом", font=("Arial", 16, "bold"), fg="#ecf0f1", bg="#2c3e50").pack(pady=10) tk.Label(self.main_frame, text="Сопоставьте описание с правильной моделью (DAC, MAC, RBAC):", font=("Arial", 12), fg="#bdc3c7", bg="#2c3e50").pack(pady=5) questions = [ ("Владелец ресурса сам назначает права (например, через ACL).", "DAC"), ("Объектам и субъектам присваиваются метки конфиденциальности, решение принимает система.", "MAC"), ("Права группируются по должностным ролям, пользователь получает доступ через роль.", "RBAC") ] self.answers = {} self.vars = [] for i, (desc, correct) in enumerate(questions): frame_q = tk.Frame(self.main_frame, bg="#2c3e50") frame_q.pack(fill=tk.X, padx=20, pady=5) tk.Label(frame_q, text=desc, wraplength=600, anchor="w", justify=tk.LEFT, fg="white", bg="#2c3e50").pack(side=tk.LEFT, padx=5) var = tk.StringVar(frame_q) var.set("") self.vars.append(var) combo = ttk.Combobox(frame_q, textvariable=var, values=["DAC", "MAC", "RBAC"], state="readonly", width=10) combo.pack(side=tk.RIGHT, padx=5) self.answers[i] = correct def check(): correct_count = 0 for i, var in enumerate(self.vars): if var.get() == self.answers[i]: correct_count += 1 if correct_count == 3: self.play_sound(True) self.animate_widget(self.main_frame, "#2c3e50", "#27ae60", steps=8) messagebox.showinfo("Верно", "Все модели определены правильно!") self.next_scene() else: self.play_sound(False) self.animate_widget(self.main_frame, "#2c3e50", "#c0392b", steps=8) messagebox.showerror("Ошибка", f"Правильно: {correct_count} из 3. Попробуйте ещё раз.") btn = tk.Button(self.main_frame, text="Проверить", command=check, bg="#3498db", fg="white", font=("Arial", 12, "bold"), relief=tk.RAISED, padx=20, pady=5) btn.pack(pady=20) hint_btn = tk.Button(self.main_frame, text="Подсказка", command=lambda: self.show_hint("DAC — владелец решает, MAC — метки, RBAC — роли"), bg="#f39c12", fg="white") hint_btn.pack(pady=5) # СЦЕНА 2: Назначение прав (используем Canvas для красивого отображения) def scene2(self): tk.Label(self.main_frame, text="СЦЕНА 2: Принцип минимальных привилегий", font=("Arial", 16, "bold"), fg="#ecf0f1", bg="#2c3e50").pack(pady=10) tk.Label(self.main_frame, text="Для каждой роли отметьте разрешённые объекты (только необходимое):", font=("Arial", 12), fg="#bdc3c7", bg="#2c3e50").pack(pady=5) roles = ["Бухгалтер", "Кадровик", "Сисадмин", "Стажёр"] objects = ["Зарплата", "Личные дела", "Журналы сервера", "Общие инструкции"] correct = [ [True, False, False, False], [False, True, False, False], [False, False, True, False], [False, False, False, True] ] self.check_vars = [] # Создаём таблицу с цветными ячейками canvas = tk.Canvas(self.main_frame, bg="#2c3e50", highlightthickness=0) canvas.pack(fill=tk.BOTH, expand=True) # Заголовки for j, obj in enumerate(objects): canvas.create_text(200 + j*120, 50, text=obj, fill="white", font=("Arial", 10, "bold")) for i, role in enumerate(roles): canvas.create_text(80, 100 + i*60, text=role, fill="#f1c40f", font=("Arial", 11, "bold"), anchor="w") self.check_boxes = [] for i in range(len(roles)): row = [] for j in range(len(objects)): var = tk.BooleanVar(value=False) chk = tk.Checkbutton(self.main_frame, variable=var, bg="#2c3e50", selectcolor="#2c3e50", activebackground="#2c3e50") # размещаем через create_window window_id = canvas.create_window(200 + j*120, 100 + i*60, window=chk, anchor=tk.CENTER) row.append(var) self.check_boxes.append(row) def check2(): ok = True for i in range(len(roles)): for j in range(len(objects)): if self.check_boxes[i][j].get() != correct[i][j]: ok = False break if not ok: break if ok: self.play_sound(True) self.animate_widget(self.main_frame, "#2c3e50", "#27ae60", steps=8) messagebox.showinfo("Верно", "Права назначены правильно!") self.next_scene() else: self.play_sound(False) self.animate_widget(self.main_frame, "#2c3e50", "#c0392b", steps=8) messagebox.showerror("Ошибка", "Нарушен принцип минимальных привилегий. Стажёр не должен видеть зарплату, а бухгалтер — журналы.") btn = tk.Button(self.main_frame, text="Проверить", command=check2, bg="#3498db", fg="white", font=("Arial", 12, "bold")) btn.pack(pady=20) hint_btn = tk.Button(self.main_frame, text="Подсказка", command=lambda: self.show_hint("Каждой роли — только её данные: бухгалтеру зарплата, кадровику личные дела и т.д."), bg="#f39c12", fg="white") hint_btn.pack(pady=5) # СЦЕНА 3: Настройка безопасной печати (с анимацией кнопок) def scene3(self): tk.Label(self.main_frame, text="СЦЕНА 3: Безопасная печать", font=("Arial", 16, "bold"), fg="#ecf0f1", bg="#2c3e50").pack(pady=10) tk.Label(self.main_frame, text="Выберите корректные параметры для печати конфиденциальных документов:", font=("Arial", 12), fg="#bdc3c7", bg="#2c3e50").pack(pady=5) # Принтер tk.Label(self.main_frame, text="Принтер:", fg="white", bg="#2c3e50").pack(anchor=tk.W) self.printer_var = tk.StringVar(value="Общий") printers = ["Общий принтер в холле", "Принтер в переговорной", "Принтер в сейфовой комнате"] for p in printers: tk.Radiobutton(self.main_frame, text=p, variable=self.printer_var, value=p, bg="#2c3e50", fg="white", selectcolor="#2c3e50").pack(anchor=tk.W) # Время удаления tk.Label(self.main_frame, text="Время автоудаления:", fg="white", bg="#2c3e50").pack(anchor=tk.W, pady=(10,0)) self.time_var = tk.IntVar(value=5) times = [5, 15, 30] for t in times: tk.Radiobutton(self.main_frame, text=f"{t} мин", variable=self.time_var, value=t, bg="#2c3e50", fg="white", selectcolor="#2c3e50").pack(anchor=tk.W) # Аутентификация self.auth_var = tk.BooleanVar(value=True) tk.Checkbutton(self.main_frame, text="Требовать аутентификацию у принтера", variable=self.auth_var, bg="#2c3e50", fg="white", selectcolor="#2c3e50").pack(anchor=tk.W) def check3(): correct = (self.printer_var.get() == "Принтер в сейфовой комнате" and self.time_var.get() == 5 and self.auth_var.get() == True) if correct: self.play_sound(True) self.animate_widget(self.main_frame, "#2c3e50", "#27ae60", steps=8) messagebox.showinfo("Верно", "Настройки безопасной печати корректны!") self.next_scene() else: self.play_sound(False) self.animate_widget(self.main_frame, "#2c3e50", "#c0392b", steps=8) messagebox.showerror("Ошибка", "Нужно выбрать принтер в сейфовой комнате, время 5 минут и обязательную аутентификацию.") btn = tk.Button(self.main_frame, text="Проверить", command=check3, bg="#3498db", fg="white", font=("Arial", 12, "bold")) btn.pack(pady=20) hint_btn = tk.Button(self.main_frame, text="Подсказка", command=lambda: self.show_hint("Конфиденциальная печать требует защищённого принтера, быстрого удаления и аутентификации."), bg="#f39c12", fg="white") hint_btn.pack(pady=5) # СЦЕНА 4: Маркировка и уничтожение (с анимированными кнопками) def scene4(self): tk.Label(self.main_frame, text="СЦЕНА 4: Маркировка и уничтожение", font=("Arial", 16, "bold"), fg="#ecf0f1", bg="#2c3e50").pack(pady=10) tk.Label(self.main_frame, text="Действия с конфиденциальными документами:", font=("Arial", 12), fg="#bdc3c7", bg="#2c3e50").pack(pady=5) tk.Label(self.main_frame, text="1. Как маркировать конфиденциальный документ?", fg="white", bg="#2c3e50").pack(anchor=tk.W) self.mark_var = tk.StringVar(value="") marks = ["Только гриф 'Конфиденциально'", "Штамп с номером экземпляра", "Без маркировки"] for m in marks: tk.Radiobutton(self.main_frame, text=m, variable=self.mark_var, value=m, bg="#2c3e50", fg="white").pack(anchor=tk.W) tk.Label(self.main_frame, text="2. Что сделать с бракованной распечаткой?", fg="white", bg="#2c3e50").pack(anchor=tk.W, pady=(10,0)) self.destroy_var = tk.StringVar(value="") destroys = ["Выбросить в мусор", "Использовать как черновик", "Уничтожить в шредере 3-й степени", "Сжечь"] for d in destroys: tk.Radiobutton(self.main_frame, text=d, variable=self.destroy_var, value=d, bg="#2c3e50", fg="white").pack(anchor=tk.W) def check4(): mark_ok = (self.mark_var.get() == "Штамп с номером экземпляра") destroy_ok = (self.destroy_var.get() == "Уничтожить в шредере 3-й степени") if mark_ok and destroy_ok: self.play_sound(True) self.animate_widget(self.main_frame, "#2c3e50", "#27ae60", steps=8) messagebox.showinfo("Верно", "Маркировка и уничтожение выполнены правильно!") self.next_scene() else: self.play_sound(False) self.animate_widget(self.main_frame, "#2c3e50", "#c0392b", steps=8) messagebox.showerror("Ошибка", "Нужен штамп с номером экземпляра и уничтожение в шредере 3-й степени.") btn = tk.Button(self.main_frame, text="Проверить", command=check4, bg="#3498db", fg="white", font=("Arial", 12, "bold")) btn.pack(pady=20) hint_btn = tk.Button(self.main_frame, text="Подсказка", command=lambda: self.show_hint("Каждый конфиденциальный документ должен иметь уникальный номер экземпляра, брак уничтожается в шредере с высокой степенью защиты."), bg="#f39c12", fg="white") hint_btn.pack(pady=5) # СЦЕНА 5: Расследование инцидента с таблицей и анимацией def scene5(self): tk.Label(self.main_frame, text="СЦЕНА 5: Расследование утечки", font=("Arial", 16, "bold"), fg="#ecf0f1", bg="#2c3e50").pack(pady=10) tk.Label(self.main_frame, text="Инцидент: Петров распечатал 'Отчет_КТ.pdf' и забыл на принтере в холле. Через 10 минут документ исчез.\nЖурнал печати:", font=("Arial", 11), fg="#bdc3c7", bg="#2c3e50").pack(pady=5) # Таблица с данными columns = ("Время", "Пользователь", "Документ", "Принтер", "Результат") tree = ttk.Treeview(self.main_frame, columns=columns, show="headings", height=6) for col in columns: tree.heading(col, text=col) tree.column(col, width=140) data = [ ("09:05", "Петров", "Отчет_КТ.pdf", "Холл", "Напечатан"), ("09:07", "Иванов", "Отчет_КТ.pdf", "Холл", "Напечатан"), ("09:10", "Сидорова", "Приказ_общий.docx", "Холл", "Напечатан"), ("09:12", "Иванов", "Список.xlsx", "Холл", "Отменено"), ("09:15", "Петров", "Счет.pdf", "Сейфовая", "Напечатан") ] for row in data: tree.insert("", tk.END, values=row) tree.pack(pady=10) tk.Label(self.main_frame, text="Кто из сотрудников распечатал тот же конфиденциальный файл сразу после Петрова?", fg="white", bg="#2c3e50").pack() self.suspect_var = tk.StringVar() suspects = ["Петров", "Иванов", "Сидорова", "Неизвестный"] for s in suspects: tk.Radiobutton(self.main_frame, text=s, variable=self.suspect_var, value=s, bg="#2c3e50", fg="white").pack(anchor=tk.W) def check5(): if self.suspect_var.get() == "Иванов": self.play_sound(True) self.animate_widget(self.main_frame, "#2c3e50", "#27ae60", steps=8) messagebox.showinfo("Верно", "Иванов распечатал тот же файл через 2 минуты, хотя его роль не позволяла печатать КТ. Нарушитель найден!") self.next_scene() else: self.play_sound(False) self.animate_widget(self.main_frame, "#2c3e50", "#c0392b", steps=8) messagebox.showerror("Ошибка", "Неверно. Посмотрите внимательно на время и документ в журнале.") btn = tk.Button(self.main_frame, text="Проверить", command=check5, bg="#3498db", fg="white", font=("Arial", 12, "bold")) btn.pack(pady=20) hint_btn = tk.Button(self.main_frame, text="Подсказка", command=lambda: self.show_hint("Обратите внимание на строку 09:07 — тот же документ, другой пользователь."), bg="#f39c12", fg="white") hint_btn.pack(pady=5) if __name__ == "__main__": root = tk.Tk() game = SecurityGame(root) root.mainloop()