97 lines
2.8 KiB
Python
97 lines
2.8 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
import pwd
|
|
import tempfile
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class AppPaths:
|
|
config_dir: Path
|
|
state_dir: Path
|
|
log_dir: Path
|
|
report_dir: Path
|
|
scenario_file: Path
|
|
app_log_file: Path
|
|
|
|
|
|
def _invoking_user() -> tuple[str, Path]:
|
|
sudo_user = os.environ.get("SUDO_USER")
|
|
if sudo_user:
|
|
user_info = pwd.getpwnam(sudo_user)
|
|
return sudo_user, Path(user_info.pw_dir)
|
|
|
|
user = os.environ.get("USER", "root")
|
|
home = Path.home()
|
|
return user, home
|
|
|
|
|
|
def build_paths() -> AppPaths:
|
|
_, user_home = _invoking_user()
|
|
config_home = Path(os.environ.get("XDG_CONFIG_HOME", user_home / ".config"))
|
|
state_home = Path(os.environ.get("XDG_STATE_HOME", user_home / ".local" / "state"))
|
|
|
|
config_dir = _select_writable_dir(
|
|
[
|
|
config_home / "securecheck",
|
|
Path.cwd() / ".securecheck-runtime" / "config",
|
|
Path(tempfile.gettempdir()) / "securecheck" / "config",
|
|
]
|
|
)
|
|
state_dir = _select_writable_dir(
|
|
[
|
|
state_home / "securecheck",
|
|
Path.cwd() / ".securecheck-runtime" / "state",
|
|
Path(tempfile.gettempdir()) / "securecheck" / "state",
|
|
]
|
|
)
|
|
|
|
if os.geteuid() == 0 and _is_path_writable(Path("/var/log")):
|
|
log_dir = Path("/var/log/securecheck")
|
|
else:
|
|
log_dir = _select_writable_dir(
|
|
[
|
|
state_dir / "logs",
|
|
Path.cwd() / ".securecheck-runtime" / "logs",
|
|
Path(tempfile.gettempdir()) / "securecheck" / "logs",
|
|
]
|
|
)
|
|
|
|
report_dir = log_dir / "reports"
|
|
scenario_file = config_dir / "scenarios.json"
|
|
app_log_file = log_dir / "securecheck.log"
|
|
return AppPaths(
|
|
config_dir=config_dir,
|
|
state_dir=state_dir,
|
|
log_dir=log_dir,
|
|
report_dir=report_dir,
|
|
scenario_file=scenario_file,
|
|
app_log_file=app_log_file,
|
|
)
|
|
|
|
|
|
def ensure_app_dirs(paths: AppPaths) -> None:
|
|
for directory in (paths.config_dir, paths.state_dir, paths.log_dir, paths.report_dir):
|
|
directory.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
def _is_path_writable(path: Path) -> bool:
|
|
target = path if path.exists() else path.parent
|
|
return os.access(target, os.W_OK)
|
|
|
|
|
|
def _select_writable_dir(candidates: list[Path]) -> Path:
|
|
for candidate in candidates:
|
|
try:
|
|
candidate.mkdir(parents=True, exist_ok=True)
|
|
probe = candidate / ".write-test"
|
|
with probe.open("w", encoding="utf-8") as handle:
|
|
handle.write("ok")
|
|
probe.unlink()
|
|
return candidate
|
|
except OSError:
|
|
continue
|
|
raise OSError("Aucun emplacement inscriptible disponible pour SecureCheck")
|