| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- 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()
|