Skip to content

Commit

Permalink
Merge branch 'release/T24.freybug.1' of github.com:the-events-calenda…
Browse files Browse the repository at this point in the history
…r/event-tickets
  • Loading branch information
bordoni committed Nov 22, 2024
2 parents 2ef1a6e + 57cab6f commit a761832
Show file tree
Hide file tree
Showing 12 changed files with 1,091 additions and 9 deletions.
5 changes: 5 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

### [5.17.0.1] 2024-11-21

* Tweak - Introduced filter `tec_tickets_rest_api_archive_results` that gives the ability to filter out the tickets being provided to the REST API archive.
* Security - Prevent Tickets from showing through REST API to unauthorized requests. [SVUL-9]

### [5.17.0] 2024-11-19

* Version - Event Tickets 5.17.0 is only compatible with Event Tickets Plus 6.1.1 or higher.
Expand Down
2 changes: 1 addition & 1 deletion event-tickets.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Plugin Name: Event Tickets
* Plugin URI: https://evnt.is/1acb
* Description: Event Tickets allows you to sell basic tickets and collect RSVPs from any post, page, or event.
* Version: 5.17.0
* Version: 5.17.0.1
* Requires at least: 6.3
* Requires PHP: 7.4
* Author: The Events Calendar
Expand Down
9 changes: 7 additions & 2 deletions readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

Contributors: theeventscalendar, brianjessee, camwynsp, redscar, tribalmike, rafsuntaskin, aguseo, bordoni, borkweb, GeoffBel, jentheo, leahkoerper, lucatume, neillmcshea, vicskf, zbtirrell, juanfra
Tags: tickets, event registration, RSVP, ticket sales, attendee management
Stable tag: 5.17.0
Stable tag: 5.17.0.1
Requires at least: 6.3
Tested up to: 6.7
Tested up to: 6.7.1
Requires PHP: 7.4
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Expand Down Expand Up @@ -203,6 +203,11 @@ Check out our extensive [knowledgebase](https://evnt.is/18wm) for articles on us

== Changelog ==

= [5.17.0.1] 2024-11-21 =

* Tweak - Introduced filter `tec_tickets_rest_api_archive_results` that gives the ability to filter out the tickets being provided to the REST API archive.
* Security - Prevent Tickets from showing through REST API to unauthorized requests. [SVUL-9]

= [5.17.0] 2024-11-19 =

* Version - Event Tickets 5.17.0 is only compatible with Event Tickets Plus 6.1.1 or higher.
Expand Down
2 changes: 1 addition & 1 deletion src/Tribe/Main.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class Tribe__Tickets__Main {
/**
* Current version of this plugin.
*/
const VERSION = '5.17.0';
const VERSION = '5.17.0.1';

/**
* Used to store the version history.
Expand Down
11 changes: 10 additions & 1 deletion src/Tribe/REST/V1/Endpoints/Ticket_Archive.php
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,16 @@ public function get( WP_REST_Request $request ) {
$data['rest_url'] = add_query_arg( $query_args, $main->get_url( '/tickets/' ) );
$data['total'] = $found;
$data['total_pages'] = $total_pages;
$data['tickets'] = $tickets;

/**
* Filters the tickets returned by the REST API.
*
* @since 5.17.0.1
*
* @param array $tickets The tickets returned by the REST API.
* @param WP_REST_Request $request The request object.
*/
$data['tickets'] = apply_filters( 'tec_tickets_rest_api_archive_results', $tickets, $request );

$headers = array(
'X-ET-TOTAL' => $data['total'],
Expand Down
54 changes: 54 additions & 0 deletions src/Tribe/REST/V1/Main.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,60 @@ public function hook() {

add_filter( 'tribe_rest_event_data', [ $this, 'rest_event_data_add_attendance' ], 10, 2 );
add_filter( 'tribe_rest_events_archive_data', [ $this, 'rest_events_archive_add_attendance' ], 10, 2 );

add_filter( 'tec_tickets_rest_api_archive_results', [ $this, 'filter_out_tickets_on_unauthorized' ], 10, 2 );
add_filter( 'tribe_rest_single_ticket_data', [ $this, 'filter_out_single_ticket_data_on_unauthorized' ], 10, 2 );
}

/**
* Filters out single ticket data that unauthorized users should not see.
*
* @since 5.17.0.1
*
* @param array $ticket_data
* @param WP_REST_Request $request
*
* @return array
*/
public function filter_out_single_ticket_data_on_unauthorized( array $ticket_data, WP_REST_Request $request ): array {
if ( $this->request_has_manage_access() ) {
return $ticket_data;
}

$ticket_validator = tribe( 'tickets.rest-v1.validator' );

if ( $ticket_validator->should_see_ticket( $ticket_data['post_id'] ?? 0, $request ) ) {
return $ticket_data;
}

return $ticket_validator->remove_ticket_data( $ticket_data );
}
/**
* Filters out tickets that unauthorized users should not see.
*
* @since 5.17.0.1
*
* @param array $tickets The tickets to filter.
* @param WP_REST_Request $request The request object.
*
* @return array The filtered tickets.
*/
public function filter_out_tickets_on_unauthorized( array $tickets, WP_REST_Request $request ) : array {
if ( $this->request_has_manage_access() ) {
return $tickets;
}

$ticket_validator = tribe( 'tickets.rest-v1.validator' );

foreach ( $tickets as $offset => $ticket ) {
if ( $ticket_validator->should_see_ticket( $ticket['post_id'] ?? 0, $request ) ) {
continue;
}

$tickets[ $offset ] = $ticket_validator->remove_ticket_data( $ticket );
}

return $tickets;
}

/**
Expand Down
78 changes: 78 additions & 0 deletions src/Tribe/REST/V1/Validator/Base.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,82 @@ class Tribe__Tickets__REST__V1__Validator__Base
extends Tribe__Tickets__Validator__Base
implements Tribe__Tickets__REST__V1__Validator__Interface {

/**
* Remove all the ticket data from ticket in rest response by authorized user.
*
* @since 5.17.0.1
*
* @param array $ticket The ticket data.
*
* @return array The ticket data with password protected fields removed.
*/
public function remove_ticket_data( array $ticket ): array {
foreach ( $ticket as $key => $val ) {
if ( is_array( $val ) || is_object( $val ) ) {
$ticket[ $key ] = $this->remove_ticket_data( (array) $val );
continue;
}

if ( is_numeric( $val ) ) {
$ticket[ $key ] = 0;
continue;
}

if ( is_bool( $val ) ) {
$ticket[ $key ] = null;
continue;
}

$ticket[ $key ] = __( 'No Access', 'event-tickets' );;
}

return $ticket;
}

/**
* Check if the ticket should be seen by the current request.
*
* @since TBD
*
* @param int $parent_id The parent's ID.
* @param WP_REST_Request $request The request object.
*
* @return bool Whether the ticket should be seen by the current user.
*/
public function should_see_ticket( int $parent_id, WP_REST_Request $request ): bool {
if ( empty( $parent_id ) ) {
$parent_id = 0;
}

$parent = get_post( $parent_id );

if ( ! ( $parent instanceof WP_Post && $parent->ID ) ) {
// Possibly parent does not exist anymore. Unauthorized should see nothing.
return false;
}

if ( ! 'publish' === $parent->post_status ) {
// Unauthorized users should not see tickets from not published events.
return false;
}

try {
$tec_validator = tribe( 'tec.rest-v1.validator' );

if ( ! method_exists( $tec_validator, 'can_access_password_content' ) ) {
// The validator is available but outdated. Better to hide data than assume its good.
throw new Exception( 'Method not found' );
}

if ( post_password_required( $parent ) && ! $tec_validator->can_access_password_content( $parent, $request ) ) {
// Unauthorized users should not see tickets from password protected events.
return false;
}
} catch ( Exception $e ) {
// If the validator is not available, we can't check the password. Fail silently hiding data.
return false;
}

return true;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php

use Tribe__Tickets__Attendee_Registration__Template;
use Tribe__Tickets__Attendee_Registration__Main;
use Tribe__Tickets__Attendee_Registration__Template as Template;
use Tribe__Tickets__Attendee_Registration__Main as Main;
use Tribe\Tests\Traits\With_Uopz;

class Template_Test extends \Codeception\TestCase\WPTestCase {
Expand All @@ -11,7 +11,7 @@ class Template_Test extends \Codeception\TestCase\WPTestCase {
* @before
*/
public function setup_singletons() {
tribe()->singleton( 'tickets.attendee_registration.template', new Tribe__Tickets__Attendee_Registration__Template() );
tribe()->singleton( 'tickets.attendee_registration.template', new Template() );
tribe()->singleton( 'tickets.attendee_registration', new Tribe__Tickets__Attendee_Registration__Main() );
}

Expand All @@ -24,7 +24,7 @@ public function it_should_return_true_if_on_custom_ar_page_with_shortcode( $wp_q
global $wp_query, $post, $shortcode_tags;

uopz_set_return( 'get_queried_object', $wp_query_update->queried_object );
uopz_set_return( Tribe__Tickets__Attendee_Registration__Main::class, 'get_slug', 'attendee-registration' );
uopz_set_return( Main::class, 'get_slug', 'attendee-registration' );

$template = tribe( 'tickets.attendee_registration.template' );

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
<?php

namespace Tribe\Tickets\REST\V1\Endpoints;

use Tribe__Tickets__REST__V1__Endpoints__Single_Ticket as Single;
use Codeception\TestCase\WPTestCase;
use Tribe\Tests\Traits\With_Clock_Mock;
use tad\Codeception\SnapshotAssertions\SnapshotAssertions;
use Tribe\Tickets\Test\Commerce\TicketsCommerce\Ticket_Maker;
use Tribe__Date_Utils as Dates;
use Prophecy\Prophecy\ObjectProphecy;

class Single_Ticket_Test extends WPTestCase {
use SnapshotAssertions;
use With_Clock_Mock;
use Ticket_Maker;

/**
* @var \Tribe__REST__Messages_Interface
*/
protected $messages;

/**
* @var \Tribe__Tickets__REST__V1__Post_Repository
*/
protected $repository;

/**
* @var \Tribe__Tickets__REST__V1__Validator__Interface
*/
protected $validator;

/**
* @return Archive
*/
private function make_instance() {
$messages = $this->messages instanceof ObjectProphecy ? $this->messages->reveal() : $this->messages;
$repository = $this->repository instanceof ObjectProphecy ? $this->repository->reveal() : $this->repository;
$validator = $this->validator instanceof ObjectProphecy ? $this->validator->reveal() : $this->validator;

return new Single( $messages, tribe( \Tribe__Tickets__REST__V1__Post_Repository::class ), $validator );
}

/**
* @test
*/
public function it_should_hide_password_protected_fields() {
$request = new \WP_REST_Request( 'GET', '' );
$this->freeze_time( Dates::immutable( '2024-06-13 17:25:00' ) );
$event_ids = [];
foreach( range( 1, 5 ) as $i ) {
$event_ids[] = tribe_events()->set_args(
[
'title' => 'Test Event ' . $i,
'status' => 'publish',
'start_date' => '2024-07-14 12:00:00',
'duration' => 2 * HOUR_IN_SECONDS,
]
)->create()->ID;
}

$ticket_ids = [];
foreach( $event_ids as $event_id ) {
$ticket_ids[] = $this->create_tc_ticket( $event_id );
}

$this->assertEquals( '2024-06-13 17:25:00', date( 'Y-m-d H:i:s' ) );

wp_update_post( [
'ID' => $event_ids[2],
'post_password' => 'password',
] );

wp_update_post( [
'ID' => $event_ids[4],
'post_password' => 'password',
] );

$sut = $this->make_instance();

$data_array = [];
foreach ( $ticket_ids as $ticket_id ) {
$request->set_param( 'id', $ticket_id );
$data_array[] = $sut->get( $request );
}

$this->assertCount( 5, $data_array );

$json = wp_json_encode( $data_array, JSON_PRETTY_PRINT );
$json = str_replace(
array_map( static fn( $id ) => '"id": ' . $id, $ticket_ids ),
'"id": "{TICKET_ID}"',
$json
);
$json = str_replace(
array_map( static fn( $id ) => '"post_id": ' . $id, $event_ids ),
'"post_id": "{EVENT_ID}"',
$json
);
$json = str_replace(
array_map( static fn( $id ) => '?id=' . $id, $event_ids ),
'?id={EVENT_ID}',
$json
);
$json = str_replace(
array_map( static fn( $id ) => 'for ' . $id, $event_ids ),
'for {EVENT_ID}',
$json
);
$json = str_replace(
array_map( static fn( $id ) => '&id=' . $id, $ticket_ids ),
'&id={TICKET_ID}',
$json
);
$json = str_replace(
array_map( static fn( $id ) => '\/events\/' . $id, $event_ids ),
'\/events\/{EVENT_ID}',
$json
);
$json = str_replace(
array_map( static fn( $id ) => '\/tickets\/' . $id, $ticket_ids ),
'\/events\/{TICKET_ID}',
$json
);
$this->assertMatchesJsonSnapshot( $json );
}
}
Loading

0 comments on commit a761832

Please sign in to comment.