Skip to content

Commit 136ae43

Browse files
authored
Do not allow uploading of binary_blob through generic POST and PUT requests. (#607)
* Add columns to other tables * Forbid uploading of binary blobs directly
1 parent b866ded commit 136ae43

File tree

3 files changed

+57
-19
lines changed

3 files changed

+57
-19
lines changed

alembic/alembic/versions/fabaaad1cf1f_make_content_url_optional_and_add_image_.py

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,47 @@
1818
branch_labels: Union[str, Sequence[str], None] = None
1919
depends_on: Union[str, Sequence[str], None] = None
2020

21+
assets_with_media = [
22+
"case_study",
23+
"computational_asset",
24+
"dataset",
25+
"educational_resource",
26+
"event",
27+
"experiment",
28+
"ml_model",
29+
"news",
30+
"organisation",
31+
"person",
32+
"project",
33+
"publication",
34+
"resource_bundle",
35+
"service",
36+
"team",
37+
]
38+
2139

2240
def upgrade() -> None:
23-
op.alter_column(
24-
"media_organisation", "content_url", existing_type=sa.String(length=1800), nullable=True
25-
)
41+
for asset_type in assets_with_media:
42+
op.alter_column(
43+
f"media_{asset_type}",
44+
"content_url",
45+
existing_type=sa.String(length=1800),
46+
nullable=True,
47+
)
2648

27-
op.add_column(
28-
table_name="media_organisation",
29-
column=sa.Column("binary_blob", sa.LargeBinary(), nullable=True),
30-
)
49+
op.add_column(
50+
table_name=f"media_{asset_type}",
51+
column=sa.Column("binary_blob", sa.LargeBinary(), nullable=True),
52+
)
3153

3254

3355
def downgrade() -> None:
34-
op.alter_column(
35-
"media_organisation", "content_url", existing_type=sa.String(length=1800), nullable=False
36-
)
56+
for asset_type in assets_with_media:
57+
op.alter_column(
58+
f"media_{asset_type}",
59+
"content_url",
60+
existing_type=sa.String(length=1800),
61+
nullable=False,
62+
)
3763

38-
op.drop_column("media_organisation", "binary_blob")
64+
op.drop_column(f"media_{asset_type}", "binary_blob")

src/database/model/ai_asset/distribution.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@ class DistributionBase(AIoDConceptBase):
5454
# Currently, only organisation accepts this field, potentially to store images (ex. organisation logo).
5555
binary_blob: bytes | None = Field(
5656
default=None,
57-
description="Binary blob for storing image (or other type of media) data.",
57+
description=(
58+
"Binary blob for storing image (or other type of media) data. "
59+
"You may not set this property directly, set it indirectly through dedicated "
60+
"endpoints such as /organisations/{identifier}/image instead."
61+
),
5862
sa_column=Column(LargeBinary),
5963
)
6064

src/routers/resource_router.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22
import datetime
33
import traceback
44
from functools import partial
5-
from http import HTTPStatus
65
from typing import Annotated, Any, Literal, Sequence, Type, TypeVar, Union, Callable, cast
7-
from wsgiref.handlers import format_date_time
86
from fastapi import APIRouter, Depends, HTTPException, status, Query, Path
97
from sqlalchemy import and_, func
108
from sqlalchemy.sql.operators import is_
@@ -25,20 +23,16 @@
2523
from database.model.concept.concept import AIoDConcept
2624
from database.model.platform.platform import Platform
2725
from database.model.platform.platform_names import PlatformName
28-
from database.model.resource_read_and_create import (
29-
resource_create,
30-
resource_read,
31-
)
3226
from database.model.serializers import deserialize_resource_relationships
3327
from database.review import Submission, SubmissionCreate
3428
from database.session import DbSession
3529
from dependencies.filtering import ResourceFilters, ResourceFiltersParams
3630
from dependencies.pagination import Pagination, PaginationParams
3731
from error_handling import as_http_exception
32+
from database.model.ai_asset.distribution import Distribution
3833
from versioning import Version, VersionedResource
3934

4035
from http import HTTPStatus
41-
from pydantic import BaseModel
4236
import base64
4337

4438
RESOURCE = TypeVar("RESOURCE", bound=AIResource)
@@ -488,6 +482,7 @@ def register_resource(
488482
detail="No permission to set platform or platform resource identifier.",
489483
)
490484

485+
_raise_if_contains_binary_blob(resource_create)
491486
try:
492487
with DbSession() as session:
493488
try:
@@ -544,6 +539,7 @@ def put_resource(
544539
with DbSession() as session:
545540
try:
546541
resource: Any = self._retrieve_resource(session, identifier)
542+
_raise_if_contains_binary_blob(resource_create_instance)
547543
if not (
548544
user_can_write(user, resource.aiod_entry)
549545
or user.has_role(f"update_{self.resource_name_plural}")
@@ -872,3 +868,15 @@ def _raise_error_on_invalid_schema(possible_schemas, schema):
872868
detail=f"Invalid schema {schema}. Expected {' or '.join(possible_schemas)}",
873869
status_code=status.HTTP_400_BAD_REQUEST,
874870
)
871+
872+
873+
def _raise_if_contains_binary_blob(item):
874+
if not hasattr(item, "media") or not (media := getattr(item, "media")):
875+
return
876+
877+
for item in media:
878+
if isinstance(item, Distribution) and item.binary_blob:
879+
raise HTTPException(
880+
status_code=HTTPStatus.BAD_REQUEST,
881+
detail="Setting `binary_blob` is forbidden. Consider using `content_url` instead.",
882+
)

0 commit comments

Comments
 (0)