Files
Aegis/backend/app/storage.py
kitos c886b6e8bb
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
fix(jira,evidence,tempo,settings): 4-issue fix batch
Jira — PoC custom field:
- Add customfield_10309 (Proof of Concept) to issue fields when creating
  test tickets so the attack procedure appears in the dedicated Jira field

Tempo — blue team exclusion:
- Remove blue_team_evaluation from _TEMPO_ACTIVITY_TYPES; blue team time
  is tracked internally (worklogs) for SLA but never sent to Tempo since
  blue team has no Jira access

Evidence — uploaded_at NULL fix:
- Set uploaded_at=datetime.utcnow() explicitly in upload_evidence router;
  the DB column has no server default so it was saving as NULL

Evidence — presigned URL browser access:
- Add MINIO_PUBLIC_ENDPOINT setting (config.py, docker-compose.prod.yml)
- storage.py uses a dedicated _public_client for presigned URL generation
  so browsers receive URLs with the publicly accessible hostname instead of
  the internal Docker service name (minio:9000)
- Expose MinIO port 9000 in docker-compose.prod.yml

Evidence — Jira attachment:
- After upload to MinIO, call jira.add_attachment() to attach the file to
  the linked Jira ticket (non-fatal; errors are logged and swallowed)

Settings — hide Jira/Tempo from blue team:
- ProfileSection checks user role; blue_lead and blue_tech do not see the
  Jira Integration or Tempo Integration personal settings sections

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 11:06:31 +02:00

85 lines
2.9 KiB
Python

"""MinIO / S3-compatible object-storage helpers.
Provides thin wrappers around boto3 for bucket management, file upload
and presigned-URL generation.
Two clients are maintained:
- ``_client`` — uses the internal Docker endpoint (``MINIO_ENDPOINT``) for
all server-side operations (upload, head_bucket, create_bucket).
- ``_public_client`` — used **only** for presigned URL generation. It uses
``MINIO_PUBLIC_ENDPOINT`` when set so the resulting URLs are reachable from
browsers. If ``MINIO_PUBLIC_ENDPOINT`` is not configured it falls back to
``MINIO_ENDPOINT`` (backwards-compatible).
"""
import boto3
from botocore.exceptions import ClientError
from app.config import settings
# ---------------------------------------------------------------------------
# Shared clients (module-level singletons)
# ---------------------------------------------------------------------------
_scheme = "https" if settings.MINIO_SECURE else "http"
# Internal client — used for uploads and bucket management
_client = boto3.client(
"s3",
endpoint_url=f"{_scheme}://{settings.MINIO_ENDPOINT}",
aws_access_key_id=settings.MINIO_ACCESS_KEY,
aws_secret_access_key=settings.MINIO_SECRET_KEY,
region_name="us-east-1", # MinIO ignores this but boto3 requires it
)
# Public client — used only for presigned URL generation so URLs contain the
# publicly accessible hostname instead of the internal Docker service name.
_public_endpoint = settings.MINIO_PUBLIC_ENDPOINT or settings.MINIO_ENDPOINT
_public_client = boto3.client(
"s3",
endpoint_url=f"{_scheme}://{_public_endpoint}",
aws_access_key_id=settings.MINIO_ACCESS_KEY,
aws_secret_access_key=settings.MINIO_SECRET_KEY,
region_name="us-east-1",
)
# ---------------------------------------------------------------------------
# Public helpers
# ---------------------------------------------------------------------------
def ensure_bucket_exists() -> None:
"""Create the evidence bucket if it does not already exist."""
try:
_client.head_bucket(Bucket=settings.MINIO_BUCKET)
except ClientError:
_client.create_bucket(Bucket=settings.MINIO_BUCKET)
def upload_file(content: bytes, key: str) -> str:
"""Upload *content* to the evidence bucket under *key*.
Returns the key that was written (same as the input).
"""
_client.put_object(
Bucket=settings.MINIO_BUCKET,
Key=key,
Body=content,
)
return key
def get_presigned_url(key: str, expiration: int = 3600) -> str:
"""Return a presigned GET URL for *key* valid for *expiration* seconds.
Uses ``_public_client`` so the URL contains the publicly accessible
hostname (``MINIO_PUBLIC_ENDPOINT``) rather than the internal Docker
service name (``MINIO_ENDPOINT``).
"""
return _public_client.generate_presigned_url(
"get_object",
Params={"Bucket": settings.MINIO_BUCKET, "Key": key},
ExpiresIn=expiration,
)