Skip to content

Commit

Permalink
* Fix url extraction with slugs that may contain user input.
Browse files Browse the repository at this point in the history
* Add slug to schedule's test factory.
* Add tests for most auth dependencies.
  • Loading branch information
MelissaAutumn committed Jun 11, 2024
1 parent 8c32443 commit 641c603
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 4 deletions.
1 change: 0 additions & 1 deletion backend/src/appointment/database/repo/schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ def verify_link(db: Session, url: str) -> models.Subscriber | None:
"""Verifies that an url belongs to a subscriber's schedule, and if so return the subscriber.
Otherwise, return none."""
username, slug, clean_url = utils.retrieve_user_url_data(url)

subscriber = repo.subscriber.get_by_username(db, username)
if not subscriber:
return None
Expand Down
2 changes: 1 addition & 1 deletion backend/src/appointment/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def setup_encryption_engine():

def retrieve_user_url_data(url):
"""Retrieves username, signature, and main url from /<username>/<signature>/"""
pattern = r"[\/]([\w\d\-_\.\@!]+)[\/]?([\w\d]*)[\/]?$"
pattern = r"[\/]([\w\d\-_\.\@!]+)[\/]?([\w\d\-_\.\@!]*)[\/]?$"
match = re.findall(pattern, url)

if match is None or len(match) == 0:
Expand Down
2 changes: 2 additions & 0 deletions backend/test/factory/schedule_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def _make_schedule(calendar_id=FAKER_RANDOM_VALUE,
weekdays=[1,2,3,4,5],
slot_duration=FAKER_RANDOM_VALUE,
meeting_link_provider=models.MeetingLinkProviderType.none,
slug=FAKER_RANDOM_VALUE,
):
with with_db() as db:
return repo.schedule.create(db, schemas.ScheduleBase(
Expand All @@ -41,6 +42,7 @@ def _make_schedule(calendar_id=FAKER_RANDOM_VALUE,
weekdays=weekdays,
slot_duration=slot_duration if factory_has_value(slot_duration) else fake.pyint(15, 60),
meeting_link_provider=meeting_link_provider,
slug=slug if factory_has_value(slug) else fake.uuid4(),
calendar_id=calendar_id if factory_has_value(calendar_id) else make_caldav_calendar(connected=True).id
))

Expand Down
80 changes: 78 additions & 2 deletions backend/test/unit/test_auth_dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import pytest
from freezegun import freeze_time

from appointment.controller.auth import signed_url_by_subscriber
from appointment.database import repo
from appointment.dependencies.auth import get_user_from_token
from appointment.exceptions.validation import InvalidTokenException
from appointment.dependencies.auth import get_user_from_token, get_subscriber, get_admin_subscriber, \
get_subscriber_from_schedule_or_signed_url
from appointment.exceptions.validation import InvalidTokenException, InvalidPermissionLevelException
from appointment.routes.auth import create_access_token


Expand Down Expand Up @@ -61,3 +63,77 @@ def test_get_user_from_token(self, with_db, with_l10n, make_pro_subscriber):
# Internally raises ExpiredSignatureError, but we catch it and send a HTTPException instead.
with pytest.raises(InvalidTokenException):
get_user_from_token(db, access_token)

def test_get_subscriber(self, with_db, with_l10n, make_pro_subscriber):
subscriber = make_pro_subscriber()
access_token = create_access_token(data={"sub": f"uid-{subscriber.id}"})

with with_db() as db:
retrieved_subscriber = get_subscriber(access_token, db)

assert retrieved_subscriber.id == subscriber.id
assert retrieved_subscriber.email == subscriber.email

def test_get_subscriber_with_invalid_token(self, with_db, with_l10n, make_pro_subscriber):
subscriber = make_pro_subscriber()

with with_db() as db:
with pytest.raises(InvalidTokenException):
# Use a nonsense value, like the subscriber id!
get_subscriber(subscriber.id, db)

def test_get_admin_subscriber(self, with_db, with_l10n, make_pro_subscriber):
subscriber = make_pro_subscriber()
access_token = create_access_token(data={"sub": f"uid-{subscriber.id}"})

os.environ['APP_ADMIN_ALLOW_LIST'] = subscriber.email

with with_db() as db:
retrieved_subscriber = get_admin_subscriber(get_subscriber(access_token, db))

assert retrieved_subscriber.id == subscriber.id
assert retrieved_subscriber.email == subscriber.email

def test_get_admin_subscriber_fails_with_no_allow_list(self, with_db, with_l10n, make_pro_subscriber):
subscriber = make_pro_subscriber()
access_token = create_access_token(data={"sub": f"uid-{subscriber.id}"})

os.environ['APP_ADMIN_ALLOW_LIST'] = ''

with with_db() as db:
with pytest.raises(InvalidPermissionLevelException):
get_admin_subscriber(get_subscriber(access_token, db))

def test_get_admin_subscriber_fails_not_in_allow_list(self, with_db, with_l10n, make_pro_subscriber):
subscriber = make_pro_subscriber(email='[email protected]')
access_token = create_access_token(data={"sub": f"uid-{subscriber.id}"})

os.environ['APP_ADMIN_ALLOW_LIST'] = '@notexample.org'

with with_db() as db:
with pytest.raises(InvalidPermissionLevelException):
get_admin_subscriber(get_subscriber(access_token, db))

def test_get_subscriber_from_schedule_or_signed_url_with_signed_url(self, with_db, with_l10n, make_pro_subscriber):
subscriber = make_pro_subscriber()

with with_db() as db:
signed_url = signed_url_by_subscriber(subscriber)
retrieved_subscriber = get_subscriber_from_schedule_or_signed_url(signed_url, db)

assert retrieved_subscriber.id == subscriber.id
assert retrieved_subscriber.email == subscriber.email

def test_get_subscriber_from_schedule_or_signed_url_with_schedule_slug(self, with_db, with_l10n,
make_pro_subscriber, make_schedule,
make_caldav_calendar):
subscriber = make_pro_subscriber()
calendar = make_caldav_calendar(subscriber_id=subscriber.id)
schedule = make_schedule(calendar_id=calendar.id)

with with_db() as db:
url = f"https://apmt.day/{subscriber.username}/{schedule.slug}/"
retrieved_subscriber = get_subscriber_from_schedule_or_signed_url(url, db)

assert retrieved_subscriber.id == subscriber.id
assert retrieved_subscriber.email == subscriber.email

0 comments on commit 641c603

Please sign in to comment.