Skip to content

Commit

Permalink
Calculate the cipher length and base64 length ahead of time. (Fixes #853
Browse files Browse the repository at this point in the history
) (#854)

* Calculate the cipher length and base64 length ahead of time. (Fixes #853)

We also fix up an existing issue with a user's schedules.details field.

* Allow details and location_url to be nullable for requests.
  • Loading branch information
MelissaAutumn authored Feb 11, 2025
1 parent 2a47a17 commit 7625b54
Show file tree
Hide file tree
Showing 5 changed files with 489 additions and 3 deletions.
11 changes: 10 additions & 1 deletion backend/src/appointment/database/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,17 @@ class TimeMode(enum.Enum):
h24 = 24


def calculate_encrypted_length(length: int):
"""Calculate the length of the string after it's been encrypted and encoded."""
cipher_length = length + 16 - (length % 16) # Fixed block size of 16 for Aes
return int((cipher_length + 2) / 3) << 2 # Base64 with padding


def encrypted_type(column_type, length: int = 255, **kwargs) -> StringEncryptedType:
"""Helper to reduce visual noise when creating model columns"""
return StringEncryptedType(column_type, secret, AesEngine, 'pkcs5', length=length, **kwargs)
return StringEncryptedType(
column_type, secret, AesEngine, 'pkcs5', length=calculate_encrypted_length(length), **kwargs
)


@as_declarative()
Expand Down Expand Up @@ -441,6 +449,7 @@ def is_available(self) -> bool:

class WaitingList(Base):
"""Holds a list of hopefully future-Appointment users"""

__tablename__ = 'waiting_list'

id = Column(Integer, primary_key=True, index=True)
Expand Down
4 changes: 2 additions & 2 deletions backend/src/appointment/database/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,8 @@ class ScheduleBase(BaseModel):
slug: Optional[str] = None
calendar_id: int
location_type: LocationType | None = LocationType.inperson
location_url: str | None = None
details: str | None = None
location_url: Annotated[str | None, Field(max_length=2048)] = None
details: Annotated[str | None, Field(max_length=250)] = None
start_date: date | None = None
end_date: date | None = None
start_time: time | None = None
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""clean invalid schedule details
Revision ID: 645fd31f827d
Revises: 4a15d01919b8
Create Date: 2025-02-05 18:39:12.277713
"""
import binascii

from alembic import op
import sqlalchemy as sa
from sqlalchemy.orm import Session
import base64

# revision identifiers, used by Alembic.
revision = '645fd31f827d'
down_revision = '4a15d01919b8'
branch_labels = None
depends_on = None


def upgrade() -> None:
session = Session(op.get_bind())
bad_fields_found = 0
rows = session.execute(sa.text('SELECT id, details FROM schedules WHERE details is not null;')).all()
for row in rows:
id, details = row
try:
base64.b64decode(details)
except binascii.Error:
bad_fields_found += 1
statement = sa.text('UPDATE schedules SET details = NULL WHERE id = :id')
statement = statement.bindparams(id=id)
op.execute(statement)

print(f'Nulled {bad_fields_found} schedules.details fields.')


def downgrade() -> None:
pass
Loading

0 comments on commit 7625b54

Please sign in to comment.