From 7738d26deaedf676f734c9e906fd694d608f56ce Mon Sep 17 00:00:00 2001 From: Mia Bennett Date: Mon, 13 Apr 2026 15:02:56 +0930 Subject: [PATCH 1/3] test(bookings): [PPT-2457] checkout other users booking --- spec/controllers/bookings_spec.cr | 35 +++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/spec/controllers/bookings_spec.cr b/spec/controllers/bookings_spec.cr index bd2d82c1..af1dfe41 100644 --- a/spec/controllers/bookings_spec.cr +++ b/spec/controllers/bookings_spec.cr @@ -2803,4 +2803,39 @@ describe Bookings do booking.booking_end.should eq(today_end) booking.all_day.should be_false end + + describe "PPT-2457", tags: "PPT-2457" do + it "#check_in should not allow a different user to check out another user's booking" do + WebMock.stub(:post, "#{ENV["PLACE_URI"]}/auth/oauth/token") + .to_return(body: File.read("./spec/fixtures/tokens/placeos_token.json")) + WebMock.stub(:post, "#{ENV["PLACE_URI"]}/api/engine/v2/signal?channel=staff/booking/changed") + .to_return(body: "") + tenant = get_tenant + + Timecop.scale(600) # 1 second == 10 minutes + + # Create a booking owned by user-one + booking = BookingsHelper.create_booking( + tenant_id: tenant.id.not_nil!, + user_email: "user-one@example.com", + zones: ["zone-1234"], + booking_start: 1.minutes.from_now.to_unix, + booking_end: 15.minutes.from_now.to_unix, + ) + + # Check in the booking as the admin (owner proxy) so it's in checked_in state + client.post("#{BOOKINGS_BASE}/#{booking.id}/check_in?state=true", headers: headers) + sleep(200.milliseconds) # advance time 2 minutes + + # A different non-admin user attempts to check out (state=false) the booking + user_two_headers = Mock::Headers.office365_normal_user(email: "user-two@example.com") + response = client.post("#{BOOKINGS_BASE}/#{booking.id}/check_in?state=false", headers: user_two_headers) + response.status_code.should eq(403) + + # Verify the booking is still checked in and was NOT checked out + body = JSON.parse(client.get("#{BOOKINGS_BASE}/#{booking.id}", headers: headers).body).as_h + body["checked_in"].should be_true + body["checked_out_at"]?.should be_nil + end + end end From 1821b9075406808abd9de3fbb9203a456f9dbb03 Mon Sep 17 00:00:00 2001 From: Mia Bennett Date: Mon, 13 Apr 2026 15:03:40 +0930 Subject: [PATCH 2/3] fix(bookings): [PPT-2457] checkout other users booking --- src/controllers/bookings.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/bookings.cr b/src/controllers/bookings.cr index e415b2f1..a5e36405 100644 --- a/src/controllers/bookings.cr +++ b/src/controllers/bookings.cr @@ -65,7 +65,7 @@ class Bookings < Application end end - @[AC::Route::Filter(:before_action, only: [:update, :update_alt, :destroy, :update_state, :update_induction, :patch_extdata])] + @[AC::Route::Filter(:before_action, only: [:update, :update_alt, :destroy, :update_state, :update_induction, :patch_extdata, :check_in])] private def confirm_access return if is_support? if user = current_user From 49aefa8ff957cfeea039a25f87578a733bd24364 Mon Sep 17 00:00:00 2001 From: Mia Bennett Date: Mon, 13 Apr 2026 15:04:45 +0930 Subject: [PATCH 3/3] chore(ameba): add ameba rules --- .ameba.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.ameba.yml b/.ameba.yml index d41f6687..4ce7101f 100644 --- a/.ameba.yml +++ b/.ameba.yml @@ -50,6 +50,13 @@ Naming/PredicateName: Documentation/DocumentationAdmonition: Enabled: false +Style/MultilineStringLiteral: + Excluded: + - spec/**/* + +Lint/BeTruthy: + Enabled: false + Lint/SpecFilename: Excluded: - spec/controllers/helpers/*