Files
wifi-gps-mapper-public/cracklab/server/web/routes.py
T
2026-10-06 21:08:12 +03:00

409 lines
10 KiB
Python

from __future__ import annotations
import json
from collections import Counter
from flask import redirect, render_template, request, url_for
from cracklab.server.db.connection import connect_database
from cracklab.server.repositories.clients import ClientsRepository
from cracklab.server.repositories.jobs import JobsRepository
from cracklab.server.repositories.methods import MethodsRepository
from cracklab.server.repositories.results import ResultsRepository
from cracklab.server.services.methods import MethodService, MethodServiceError
from cracklab.server.web import cracklab_bp
@cracklab_bp.route("/")
def dashboard():
conn = connect_database()
try:
methods = MethodsRepository(conn).list_methods()
jobs = JobsRepository(conn).list_jobs()
clients = ClientsRepository(conn).list_clients()
results = ResultsRepository(conn).list_results()
finally:
conn.close()
job_statuses = Counter(
row["status"]
for row in jobs
)
recent_jobs = jobs[:10]
method_names = {
row["id"]: row["name"]
for row in methods
}
client_names = {
row["id"]: row["name"]
for row in clients
}
return render_template(
"dashboard.html",
active_page="dashboard",
methods_count=len(methods),
jobs_count=len(jobs),
clients_count=len(clients),
results_count=len(results),
active_jobs_count=sum(
job_statuses.get(status, 0)
for status in (
"CREATED",
"QUEUED",
"RUNNING",
"PARTIAL",
)
),
job_statuses=job_statuses,
recent_jobs=recent_jobs,
method_names=method_names,
client_names=client_names,
)
@cracklab_bp.route("/jobs/")
def jobs_page():
return _placeholder(
"Jobs",
"Job management will be implemented here.",
"jobs",
)
@cracklab_bp.route("/methods/")
def methods_page():
conn = connect_database()
try:
service = MethodService(conn)
methods = service.list_methods()
method_rows = []
for method in methods:
versions = service.list_method_versions(method["id"])
latest = versions[-1] if versions else None
steps_count = 0
if latest is not None:
try:
definition = json.loads(
latest["definition_json"]
)
steps_count = len(
definition.get("steps", [])
)
except (TypeError, ValueError):
steps_count = 0
method_rows.append(
{
"id": method["id"],
"name": method["name"],
"description": method["description"],
"created_at": method["created_at"],
"version": (
latest["version"]
if latest is not None
else None
),
"steps_count": steps_count,
}
)
finally:
conn.close()
return render_template(
"methods.html",
active_page="methods",
methods=method_rows,
)
@cracklab_bp.route("/methods/create/", methods=["GET", "POST"])
def method_create_page():
error = None
if request.method == "POST":
try:
name = request.form.get("name", "").strip()
description = request.form.get(
"description",
"",
).strip()
hash_mode_raw = request.form.get(
"hash_mode",
"22000",
).strip()
if not name:
raise MethodServiceError(
"Введите название метода."
)
if hash_mode_raw != "22000":
raise MethodServiceError(
"Сейчас поддерживается только hash mode 22000."
)
hash_mode = 22000
steps = _build_steps_from_form()
definition = {
"schema_version": 1,
"hash_mode": hash_mode,
"steps": steps,
}
conn = connect_database()
try:
service = MethodService(conn)
with conn:
method_id = service.create_method(
name=name,
description=description or None,
)
service.create_method_version(
method_id=method_id,
version=1,
definition=definition,
)
finally:
conn.close()
return redirect(
url_for(
"cracklab.methods_page",
)
)
except (MethodServiceError, ValueError) as exc:
error = str(exc)
return render_template(
"method_create.html",
active_page="methods",
error=error,
)
def _build_steps_from_form() -> list[dict]:
step_count_raw = request.form.get(
"step_count",
"1",
).strip()
try:
step_count = int(step_count_raw)
except ValueError as exc:
raise MethodServiceError(
"Некорректное количество шагов."
) from exc
if step_count < 1:
raise MethodServiceError(
"Метод должен содержать хотя бы один шаг."
)
steps = []
for index in range(1, step_count + 1):
prefix = f"step_{index}"
attack_mode_raw = request.form.get(
f"{prefix}_attack_mode",
"",
).strip()
if not attack_mode_raw:
raise MethodServiceError(
f"Для шага {index} не выбран attack mode."
)
try:
attack_mode = int(attack_mode_raw)
except ValueError as exc:
raise MethodServiceError(
f"Некорректный attack mode в шаге {index}."
) from exc
if attack_mode not in (0, 1, 3, 6, 7):
raise MethodServiceError(
f"Attack mode {attack_mode} "
f"не поддерживается клиентом CrackLab."
)
step = {
"step_id": f"step-{index:03d}",
"attack_mode": attack_mode,
}
workload_raw = request.form.get(
f"{prefix}_workload_profile",
"2",
).strip()
if workload_raw:
try:
workload_profile = int(workload_raw)
except ValueError as exc:
raise MethodServiceError(
f"Некорректный workload profile "
f"в шаге {index}."
) from exc
if workload_profile not in (1, 2, 3, 4):
raise MethodServiceError(
f"Workload profile в шаге {index} "
"должен быть от 1 до 4."
)
step["workload_profile"] = workload_profile
if attack_mode == 0:
step["dictionary"] = {
"resource": _get_dictionary(
prefix,
"dictionary",
index,
),
}
elif attack_mode == 1:
step["dictionaries"] = [
{
"resource": _get_dictionary(
prefix,
"dictionary_1",
index,
),
},
{
"resource": _get_dictionary(
prefix,
"dictionary_2",
index,
),
},
]
elif attack_mode == 3:
step["mask"] = _get_mask(
prefix,
index,
)
elif attack_mode == 6:
step["dictionary"] = {
"resource": _get_dictionary(
prefix,
"dictionary",
index,
),
}
step["mask"] = _get_mask(
prefix,
index,
)
elif attack_mode == 7:
step["mask"] = _get_mask(
prefix,
index,
)
step["dictionary"] = {
"resource": _get_dictionary(
prefix,
"dictionary",
index,
),
}
steps.append(step)
return steps
def _get_dictionary(
prefix: str,
field: str,
index: int,
) -> str:
value = request.form.get(
f"{prefix}_{field}",
"",
).strip()
if not value:
raise MethodServiceError(
f"Для шага {index} необходимо указать словарь."
)
if not value.startswith("wordlist:"):
value = f"wordlist:{value}"
return value
def _get_mask(
prefix: str,
index: int,
) -> str:
value = request.form.get(
f"{prefix}_mask",
"",
).strip()
if not value:
raise MethodServiceError(
f"Для шага {index} необходимо указать маску."
)
return value
@cracklab_bp.route("/clients/")
def clients_page():
return _placeholder(
"Clients",
"Client management will be implemented here.",
"clients",
)
@cracklab_bp.route("/results/")
def results_page():
return _placeholder(
"Results",
"Result history will be implemented here.",
"results",
)
def _placeholder(
title: str,
description: str,
active_page: str,
):
return render_template(
"placeholder.html",
active_page=active_page,
title=title,
description=description,
)