whitehat-security/src/whitehat_security/fixtures.py

92 lines
3.8 KiB
Python
Raw Normal View History

from __future__ import annotations
from copy import deepcopy
from dataclasses import dataclass
from .differential import DifferentialProbe
from .model import Response
@dataclass
class FixtureService:
enforce_tenant: bool
def __post_init__(self) -> None:
self.objects = {
"object-a": {"id": "object-a", "tenant": "tenant-a", "label": "fixture-a"},
"object-b": {"id": "object-b", "tenant": "tenant-b", "label": "fixture-b"},
}
def snapshot_b(self) -> object:
return deepcopy(self.objects.get("object-b"))
def read(self, actor_tenant: str, object_id: str) -> Response:
item = self.objects.get(object_id)
if item is None or (self.enforce_tenant and item["tenant"] != actor_tenant):
return Response(404, {"error": "not_found"})
return Response(200, deepcopy(item))
def list_for(self, actor_tenant: str, requested_tenant: str) -> Response:
tenant = actor_tenant if self.enforce_tenant else requested_tenant
items = [deepcopy(item) for item in self.objects.values() if item["tenant"] == tenant]
return Response(200, {"items": items})
def create_for(self, actor_tenant: str, requested_tenant: str, object_id: str) -> Response:
if self.enforce_tenant and actor_tenant != requested_tenant:
return Response(403, {"error": "forbidden"})
self.objects[object_id] = {
"id": object_id,
"tenant": requested_tenant,
"label": "fixture-created",
}
return Response(201, {"id": object_id, "tenant": requested_tenant})
def update(self, actor_tenant: str, object_id: str) -> Response:
item = self.objects.get(object_id)
if item is None or (self.enforce_tenant and item["tenant"] != actor_tenant):
return Response(404, {"error": "not_found"})
item["label"] = "fixture-mutated"
return Response(200, deepcopy(item))
def delete(self, actor_tenant: str, object_id: str) -> Response:
item = self.objects.get(object_id)
if item is None or (self.enforce_tenant and item["tenant"] != actor_tenant):
return Response(404, {"error": "not_found"})
del self.objects[object_id]
return Response(200, {"deleted": object_id})
def probe_suite(service: FixtureService) -> list[DifferentialProbe]:
markers = ("object-b", "tenant-b", "fixture-b", "fixture-mutated")
return [
DifferentialProbe(
"read-by-id", "read", lambda: service.read("tenant-b", "object-b"),
lambda: service.read("tenant-a", "object-b"),
lambda: service.read("tenant-a", "absent"), markers,
),
DifferentialProbe(
"list-filter", "read", lambda: service.list_for("tenant-b", "tenant-b"),
lambda: service.list_for("tenant-a", "tenant-b"),
lambda: Response(200, {"items": []}), markers, hide_existence=False,
),
DifferentialProbe(
"create-attributed-to-b", "create",
lambda: Response(201, {"id": "owner-control"}),
lambda: service.create_for("tenant-a", "tenant-b", "object-created"),
lambda: Response(403, {"error": "forbidden"}), markers + ("object-created",),
state=lambda: deepcopy(service.objects), hide_existence=False,
),
DifferentialProbe(
"update-b", "update", lambda: Response(200, service.snapshot_b()),
lambda: service.update("tenant-a", "object-b"),
lambda: service.read("tenant-a", "absent"), markers,
state=service.snapshot_b,
),
DifferentialProbe(
"delete-b", "delete", lambda: Response(200, service.snapshot_b()),
lambda: service.delete("tenant-a", "object-b"),
lambda: service.read("tenant-a", "absent"), markers,
state=service.snapshot_b,
),
]