Update public source
This commit is contained in:
@@ -0,0 +1,408 @@
|
||||
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,
|
||||
)
|
||||
Reference in New Issue
Block a user