Skip to content

Commit dc29aad

Browse files
mglamanmglaman
authored andcommitted
Issue #3134493 by skyredwang, mglaman, introfini: The user needs "Administer orders" permission to filter the Order resource
1 parent 9db7065 commit dc29aad

File tree

2 files changed

+293
-0
lines changed

2 files changed

+293
-0
lines changed

modules/order/commerce_order.module

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use Drupal\commerce_order\Entity\OrderTypeInterface;
99
use Drupal\commerce_order\Plugin\Field\FieldFormatter\PriceCalculatedFormatter;
1010
use Drupal\Core\Access\AccessResult;
1111
use Drupal\Core\Entity\EntityInterface;
12+
use Drupal\Core\Entity\EntityTypeInterface;
1213
use Drupal\Core\Form\FormStateInterface;
1314
use Drupal\Core\Render\Element;
1415
use Drupal\Core\Session\AccountInterface;
@@ -380,3 +381,20 @@ function commerce_order_form_field_config_edit_form_alter(array &$form, FormStat
380381
}
381382
}
382383
}
384+
385+
/**
386+
* Implements hook_jsonapi_ENTITY_TYPE_filter_access().
387+
*/
388+
function commerce_order_jsonapi_commerce_order_filter_access(EntityTypeInterface $entity_type, AccountInterface $account) {
389+
// Entity API automatically hooks into the JSON:API query filter system for
390+
// entities that has a permission_provider and query_handler. However, orders
391+
// do not have an `owner` key and are not evaluated for the
392+
// JSONAPI_FILTER_AMONG_OWN check. This means only JSONAPI_FILTER_AMONG_ALL is
393+
// evaluated, which defaults to the admin permission.
394+
//
395+
// Since we have a query_handler configured and inaccessible entities will
396+
// be filtered out automatically, we set it to allowed.
397+
return ([
398+
JSONAPI_FILTER_AMONG_ALL => AccessResult::allowed(),
399+
]);
400+
}
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
<?php
2+
3+
namespace Drupal\Tests\commerce_order\Kernel\Jsonapi;
4+
5+
use Drupal\commerce_order\Entity\Order;
6+
use Drupal\commerce_order\Entity\OrderItem;
7+
use Drupal\commerce_price\Price;
8+
use Drupal\Component\Serialization\Json;
9+
use Drupal\Core\Url;
10+
use Drupal\Tests\commerce_order\Kernel\OrderKernelTestBase;
11+
use Drupal\user\Entity\Role;
12+
use Symfony\Component\HttpFoundation\Request;
13+
use Symfony\Component\HttpFoundation\Response;
14+
15+
/**
16+
* @group commerce_order
17+
*/
18+
class OrderCollectionFilterTest extends OrderKernelTestBase {
19+
20+
/**
21+
* The test customer with orders for testing.
22+
*
23+
* @var \Drupal\user\UserInterface
24+
*/
25+
private $testOrderCustomer;
26+
27+
/**
28+
* The other test customer.
29+
*
30+
* @var \Drupal\user\UserInterface
31+
*/
32+
private $testOtherCustomer;
33+
34+
/**
35+
* The test draft order UUID.
36+
*/
37+
const ORDER_CUSTOMER_DRAFT_UUID = '3b7ad95f-0f1c-49d9-83d5-a92460fc82f1';
38+
39+
/**
40+
* The test completed order UUID.
41+
*/
42+
const ORDER_CUSTOMER_COMPLETED_UUID = '56843d3c-31ec-40b3-8d63-38154c8a95c6';
43+
44+
/**
45+
* The order customer completed order UUID.
46+
*/
47+
const OTHER_CUSTOMER_COMPLETED_UUID = 'f3feb4c5-5266-4f74-8c21-9c02185807db';
48+
49+
/**
50+
* The anonymous order completed UUID.
51+
*/
52+
const ANONYMOUS_COMPLETED_UUID = '310a3cee-787d-43ef-b2b7-a7a37e32080a';
53+
54+
/**
55+
* {@inheritdoc}
56+
*/
57+
public static $modules = [
58+
'serialization',
59+
'jsonapi',
60+
];
61+
62+
/**
63+
* {@inheritdoc}
64+
*/
65+
protected function setUp() {
66+
parent::setUp();
67+
$this->installConfig('user');
68+
$this->grantPermissions(Role::load(Role::AUTHENTICATED_ID), ['view own commerce_order']);
69+
70+
// Create uid1.
71+
$this->createUser();
72+
$this->testOrderCustomer = $this->createUser();
73+
$this->testOtherCustomer = $this->createUser();
74+
}
75+
76+
/**
77+
* Tests filtering orders.
78+
*
79+
* @dataProvider filterDataParameters
80+
*/
81+
public function testCustomerOrderCollectionFiltering(
82+
string $user_type,
83+
int $expected_unfiltered_count,
84+
array $expected_unfiltered_uuids,
85+
int $expected_filtered_count,
86+
array $expected_filtered_uuids
87+
) {
88+
$user_ids = [
89+
'order_customer' => $this->testOrderCustomer->id(),
90+
'other_customer' => $this->testOtherCustomer->id(),
91+
'guest_customer' => 0,
92+
'guest_customer_with_permission' => 0,
93+
'admin_user' => $this->createUser([], ['administer commerce_order'])->id(),
94+
'view_user' => $this->createUser([], ['view commerce_order'])->id(),
95+
];
96+
$this->assertArrayHasKey($user_type, $user_ids);
97+
if ($user_type === 'guest_customer_with_permission') {
98+
$this->grantPermissions(Role::load(Role::ANONYMOUS_ID), ['view own commerce_order']);
99+
}
100+
101+
$this->generateTestOrders();
102+
$url = Url::fromRoute('jsonapi.commerce_order--default.collection');
103+
104+
$this->container->get('session')->set('uid', $user_ids[$user_type]);
105+
$document = $this->doRequest($url);
106+
$this->assertArrayHasKey('data', $document);
107+
$this->assertCount($expected_unfiltered_count, $document['data'], var_export($document['data'], TRUE));
108+
$this->assertEquals($expected_unfiltered_uuids, array_map(static function (array $item) {
109+
return $item['id'];
110+
}, $document['data']));
111+
112+
$url->setOption('query', [
113+
'filter' => [
114+
'state' => 'completed',
115+
],
116+
]);
117+
$document = $this->doRequest($url);
118+
$this->assertArrayHasKey('data', $document);
119+
$this->assertCount($expected_filtered_count, $document['data'], var_export($document['data'], TRUE));
120+
$this->assertEquals($expected_filtered_uuids, array_map(static function (array $item) {
121+
return $item['id'];
122+
}, $document['data']));
123+
}
124+
125+
public function filterDataParameters(): \Generator {
126+
yield [
127+
'order_customer',
128+
2,
129+
[self::ORDER_CUSTOMER_DRAFT_UUID, self::ORDER_CUSTOMER_COMPLETED_UUID],
130+
1,
131+
[self::ORDER_CUSTOMER_COMPLETED_UUID],
132+
];
133+
yield [
134+
'other_customer',
135+
1,
136+
[self::OTHER_CUSTOMER_COMPLETED_UUID],
137+
1,
138+
[self::OTHER_CUSTOMER_COMPLETED_UUID],
139+
];
140+
yield [
141+
'guest_customer',
142+
0,
143+
[],
144+
0,
145+
[],
146+
];
147+
yield [
148+
'guest_customer_with_permission',
149+
0,
150+
[],
151+
0,
152+
[],
153+
];
154+
yield [
155+
'admin_user',
156+
4,
157+
[
158+
self::ORDER_CUSTOMER_DRAFT_UUID,
159+
self::ORDER_CUSTOMER_COMPLETED_UUID,
160+
self::OTHER_CUSTOMER_COMPLETED_UUID,
161+
self::ANONYMOUS_COMPLETED_UUID,
162+
],
163+
3,
164+
[
165+
self::ORDER_CUSTOMER_COMPLETED_UUID,
166+
self::OTHER_CUSTOMER_COMPLETED_UUID,
167+
self::ANONYMOUS_COMPLETED_UUID,
168+
],
169+
];
170+
yield [
171+
'view_user',
172+
4,
173+
[
174+
self::ORDER_CUSTOMER_DRAFT_UUID,
175+
self::ORDER_CUSTOMER_COMPLETED_UUID,
176+
self::OTHER_CUSTOMER_COMPLETED_UUID,
177+
self::ANONYMOUS_COMPLETED_UUID,
178+
],
179+
3,
180+
[
181+
self::ORDER_CUSTOMER_COMPLETED_UUID,
182+
self::OTHER_CUSTOMER_COMPLETED_UUID,
183+
self::ANONYMOUS_COMPLETED_UUID,
184+
],
185+
];
186+
}
187+
188+
/**
189+
* Generates four test orders.
190+
*
191+
* 1. Draft order owned by test customer.
192+
* 2. Completed order owned by test customer.
193+
* 3. Completed order owned by other customer.
194+
* 4. Completed anonymous order.
195+
*/
196+
private function generateTestOrders() {
197+
Order::create([
198+
'uuid' => self::ORDER_CUSTOMER_DRAFT_UUID,
199+
'type' => 'default',
200+
'store_id' => $this->store->id(),
201+
'state' => 'draft',
202+
'mail' => $this->testOrderCustomer->getEmail(),
203+
'uid' => $this->testOrderCustomer->id(),
204+
'ip_address' => '127.0.0.1',
205+
'order_items' => [$this->generateOrderItem()],
206+
])->save();
207+
Order::create([
208+
'uuid' => self::ORDER_CUSTOMER_COMPLETED_UUID,
209+
'type' => 'default',
210+
'store_id' => $this->store->id(),
211+
'state' => 'completed',
212+
'mail' => $this->testOrderCustomer->getEmail(),
213+
'uid' => $this->testOrderCustomer->id(),
214+
'ip_address' => '127.0.0.1',
215+
'order_items' => [$this->generateOrderItem()],
216+
])->save();
217+
Order::create([
218+
'uuid' => self::OTHER_CUSTOMER_COMPLETED_UUID,
219+
'type' => 'default',
220+
'store_id' => $this->store->id(),
221+
'state' => 'completed',
222+
'mail' => $this->testOtherCustomer->getEmail(),
223+
'uid' => $this->testOtherCustomer->id(),
224+
'ip_address' => '127.0.0.1',
225+
'order_items' => [$this->generateOrderItem()],
226+
])->save();
227+
Order::create([
228+
'uuid' => self::ANONYMOUS_COMPLETED_UUID,
229+
'type' => 'default',
230+
'store_id' => $this->store->id(),
231+
'state' => 'completed',
232+
'mail' => 'foo@bar.com',
233+
'uid' => 0,
234+
'ip_address' => '127.0.0.1',
235+
'order_items' => [$this->generateOrderItem()],
236+
])->save();
237+
}
238+
239+
/**
240+
* Generates a test order item.
241+
*
242+
* @return \Drupal\commerce_order\Entity\OrderItemInterface
243+
* The order item.
244+
*/
245+
private function generateOrderItem() {
246+
$order_item = OrderItem::create([
247+
'type' => 'test',
248+
'quantity' => 1,
249+
'unit_price' => new Price('12.00', 'USD'),
250+
]);
251+
$order_item->save();
252+
return $this->reloadEntity($order_item);
253+
}
254+
255+
/**
256+
* Does a request.
257+
*
258+
* @param \Drupal\Core\Url $url
259+
* The URL.
260+
*
261+
* @return array
262+
* The decoded response JSON.
263+
*/
264+
private function doRequest(Url $url) {
265+
$request = Request::create($url->toString(), 'GET');
266+
$request->setSession($this->container->get('session'));
267+
$session_cookie_name = 'SESS' . substr(hash('sha256', drupal_valid_test_ua()), 0, 32);
268+
$request->cookies->set($session_cookie_name, $request->getSession()->getId());
269+
$request->headers->set('Accept', 'application/vnd.api+json');
270+
$response = $this->container->get('http_kernel')->handle($request);
271+
assert($response instanceof Response);
272+
return Json::decode($response->getContent());
273+
}
274+
275+
}

0 commit comments

Comments
 (0)