Skip to content

Commit 0685c50

Browse files
Fix assets dropdowns visibility from self service
1 parent 53d3642 commit 0685c50

File tree

2 files changed

+166
-25
lines changed

2 files changed

+166
-25
lines changed

src/Glpi/Features/AssignableItem.php

Lines changed: 80 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535

3636
namespace Glpi\Features;
3737

38+
use CommonITILObject;
3839
use Glpi\DBAL\QueryExpression;
3940
use Glpi\DBAL\QuerySubQuery;
4041
use Group_Item;
@@ -105,8 +106,26 @@ public function canUpdateItem(): bool
105106
* @return array[]|QueryExpression[]
106107
* @see AssignableItemInterface::getAssignableVisiblityCriteria()
107108
*/
108-
public static function getAssignableVisiblityCriteria(?string $item_table_reference = null): array
109-
{
109+
public static function getAssignableVisiblityCriteria(
110+
?string $item_table_reference = null
111+
): array {
112+
$criteria = Session::getCurrentInterface() === "central"
113+
? self::getAssignableVisiblityCriteriaForCentral($item_table_reference)
114+
: self::getAssignableVisiblityCriteriaForHelpdesk($item_table_reference)
115+
;
116+
117+
// Add another layer to the array to prevent losing duplicates keys if the
118+
// result of the function is merged with another array
119+
return [crc32(serialize($criteria)) => $criteria];
120+
}
121+
122+
/**
123+
* @param string|null $item_table_reference
124+
* @return array[]|QueryExpression[]
125+
*/
126+
private static function getAssignableVisiblityCriteriaForCentral(
127+
?string $item_table_reference = null
128+
): array {
110129
if (!Session::haveRightsOr(static::$rightname, [READ, READ_ASSIGNED, READ_OWNED])) {
111130
return [new QueryExpression('0')];
112131
}
@@ -138,29 +157,69 @@ public static function getAssignableVisiblityCriteria(?string $item_table_refere
138157
}
139158
}
140159
if (Session::haveRight(static::$rightname, READ_OWNED)) {
160+
$or += self::getOwnAssetsCriteria($item_table, $relation_table);
161+
}
162+
163+
return ['OR' => $or];
164+
}
165+
166+
/**
167+
* @param string|null $item_table_reference
168+
* @return array[]|QueryExpression[]
169+
*/
170+
private static function getAssignableVisiblityCriteriaForHelpdesk(
171+
?string $item_table_reference = null
172+
): array {
173+
// Helpdesk doesn't support READ, READ_ASSIGNED, READ_OWNED rights.
174+
// Instead, we will directly check the helpdesk_hardware and
175+
// helpdesk_item_type properties from the profile
176+
$profile = Session::getCurrentProfile();
177+
178+
$raw_allowed_itemtypes = $profile->fields['helpdesk_item_type'];
179+
$allowed_itemtypes = importArrayFromDB($raw_allowed_itemtypes);
180+
if (!in_array(static::class, $allowed_itemtypes)) {
181+
return [new QueryExpression('0')];
182+
}
183+
184+
$raw_rights = $profile->fields['helpdesk_hardware'];
185+
$all_assets = $raw_rights & (2 ** CommonITILObject::HELPDESK_ALL_HARDWARE);
186+
if ($all_assets) {
187+
return [new QueryExpression('1')];
188+
}
189+
190+
$my_assets = $raw_rights & (2 ** CommonITILObject::HELPDESK_MY_HARDWARE);
191+
if ($my_assets) {
192+
$item_table = $item_table_reference ?? static::getTable();
193+
$relation_table = Group_Item::getTable();
194+
$or = self::getOwnAssetsCriteria($item_table, $relation_table);
195+
196+
return ['OR' => $or];
197+
}
198+
199+
// User can't see any assets
200+
return [new QueryExpression('0')];
201+
}
202+
203+
private static function getOwnAssetsCriteria(
204+
string $item_table,
205+
string $relation_table,
206+
): array {
207+
$or = [$item_table . '.users_id' => $_SESSION['glpiID']];
208+
if (count($_SESSION['glpigroups']) > 0) {
141209
$or[] = [
142-
$item_table . '.users_id' => $_SESSION['glpiID'],
210+
$item_table . '.id' => new QuerySubQuery([
211+
'SELECT' => $relation_table . '.items_id',
212+
'FROM' => $relation_table,
213+
'WHERE' => [
214+
'itemtype' => static::class,
215+
'groups_id' => $_SESSION['glpigroups'],
216+
'type' => Group_Item::GROUP_TYPE_NORMAL,
217+
],
218+
]),
143219
];
144-
if (count($_SESSION['glpigroups']) > 0) {
145-
$or[] = [
146-
$item_table . '.id' => new QuerySubQuery([
147-
'SELECT' => $relation_table . '.items_id',
148-
'FROM' => $relation_table,
149-
'WHERE' => [
150-
'itemtype' => static::class,
151-
'groups_id' => $_SESSION['glpigroups'],
152-
'type' => Group_Item::GROUP_TYPE_NORMAL,
153-
],
154-
]),
155-
];
156-
}
157220
}
158221

159-
// Add another layer to the array to prevent losing duplicates keys if the
160-
// result of the function is merged with another array
161-
$criteria = [crc32(serialize($or)) => ['OR' => $or]];
162-
163-
return $criteria;
222+
return $or;
164223
}
165224

166225
/** @see AssignableItemInterface::getRights() */

tests/functional/DropdownTest.php

Lines changed: 86 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,10 @@
3535
namespace tests\units;
3636

3737
use CommonDBTM;
38+
use CommonITILObject;
3839
use Computer;
3940
use DbTestCase;
41+
use Entity;
4042
use Generator;
4143
use Glpi\Asset\Asset_PeripheralAsset;
4244
use Glpi\Features\AssignableItem;
@@ -45,9 +47,11 @@
4547
use Item_DeviceSimcard;
4648
use Monitor;
4749
use PHPUnit\Framework\Attributes\DataProvider;
50+
use Profile;
4851
use Session;
4952
use State;
5053
use Symfony\Component\DomCrawler\Crawler;
54+
use Ticket;
5155
use User;
5256

5357
/* Test for inc/dropdown.class.php */
@@ -2569,20 +2573,20 @@ public function testSupplierActorDropdownOnlyActive()
25692573
'is_recursive' => 1,
25702574
]);
25712575
$params = [
2572-
'itemtype' => \Ticket::class,
2576+
'itemtype' => Ticket::class,
25732577
'actortype' => 'assign',
25742578
'returned_itemtypes' => [\Supplier::class],
25752579
'searchText' => '',
25762580
];
2577-
$results = \Dropdown::getDropdownActors($params + ['_idor_token' => Session::getNewIDORToken(\Ticket::class, $params)], false);
2581+
$results = \Dropdown::getDropdownActors($params + ['_idor_token' => Session::getNewIDORToken(Ticket::class, $params)], false);
25782582
$this->assertNotEmpty($results['results'][0]['children']);
25792583
$this->assertCount(0, array_filter($results['results'][0]['children'], function ($result) use ($inactive_supplier) {
25802584
return $result['id'] === \Supplier::class . '_' . $inactive_supplier->getID();
25812585
}));
25822586

25832587
// If asking for inactive_deleted, it should return the inactive supplier
25842588
$params['inactive_deleted'] = 1;
2585-
$results = \Dropdown::getDropdownActors($params + ['_idor_token' => Session::getNewIDORToken(\Ticket::class, $params)], false);
2589+
$results = \Dropdown::getDropdownActors($params + ['_idor_token' => Session::getNewIDORToken(Ticket::class, $params)], false);
25862590
$this->assertNotEmpty($results['results'][0]['children']);
25872591
$this->assertCount(1, array_filter($results['results'][0]['children'], function ($result) use ($inactive_supplier) {
25882592
return $result['id'] === \Supplier::class . '_' . $inactive_supplier->getID();
@@ -2685,7 +2689,7 @@ public function testGetDropdownMyDevices()
26852689
]);
26862690

26872691
// Ensure proper permissions and helpdesk types
2688-
$_SESSION["glpiactiveprofile"]["helpdesk_hardware"] = pow(2, \Ticket::HELPDESK_MY_HARDWARE);
2692+
$_SESSION["glpiactiveprofile"]["helpdesk_hardware"] = pow(2, Ticket::HELPDESK_MY_HARDWARE);
26892693
$_SESSION["glpiactiveprofile"]["helpdesk_item_type"] = ['Computer', 'Monitor', 'Printer'];
26902694

26912695
$post = [
@@ -2768,4 +2772,82 @@ public function testGetDropdownMyDevices()
27682772
// Test that count is accurate
27692773
$this->assertGreaterThan(0, $result['count']);
27702774
}
2775+
2776+
public static function assetsDropdownForHelpdeskProvider(): iterable
2777+
{
2778+
yield 'no rights' => [
2779+
'can_view' => 0,
2780+
'itemtypes' => [Computer::class],
2781+
'expected' => [],
2782+
];
2783+
yield 'see his own computers' => [
2784+
'can_view' => 2 ** CommonITILObject::HELPDESK_MY_HARDWARE,
2785+
'itemtypes' => [Computer::class],
2786+
'expected' => ['My computer'],
2787+
];
2788+
yield 'see all computers' => [
2789+
'can_view' => 2 ** CommonITILObject::HELPDESK_ALL_HARDWARE,
2790+
'itemtypes' => [Computer::class],
2791+
'expected' => ['My computer', 'Not my computer'],
2792+
];
2793+
yield 'see all monitors' => [
2794+
'can_view' => 2 ** CommonITILObject::HELPDESK_ALL_HARDWARE,
2795+
'itemtypes' => [Monitor::class],
2796+
'expected' => [],
2797+
];
2798+
}
2799+
2800+
#[DataProvider('assetsDropdownForHelpdeskProvider')]
2801+
public function testAssetsDropdownForHelpdesk(
2802+
int $can_view,
2803+
array $itemtypes,
2804+
array $expected,
2805+
): void {
2806+
// Arrange: assign a computer to a self-service user and set up the
2807+
// profile with the given rights.
2808+
// Wrap items in an entity for better test isolation
2809+
$this->login(); // Need to be logged in to create an entity
2810+
$entity = $this->createItem(Entity::class, [
2811+
'name' => 'My entity',
2812+
'entities_id' => $this->getTestRootEntity(only_id: true),
2813+
]);
2814+
$this->logOut();
2815+
$this->createItem(Computer::class, [
2816+
'name' => 'My computer',
2817+
'entities_id' => $entity->getID(),
2818+
'users_id' => getItemByTypeName(User::class, "post-only", true),
2819+
]);
2820+
$this->createItem(Computer::class, [
2821+
'name' => 'Not my computer',
2822+
'entities_id' => $entity->getID(),
2823+
]);
2824+
$this->updateItem(
2825+
Profile::class,
2826+
getItemByTypeName(Profile::class, 'Self-Service', onlyid: true),
2827+
[
2828+
'helpdesk_hardware' => $can_view,
2829+
'helpdesk_item_type' => $itemtypes,
2830+
],
2831+
['helpdesk_item_type'],
2832+
);
2833+
2834+
// Act: get dropdown values for this user
2835+
$this->login('post-only');
2836+
$this->setEntity("My entity", false);
2837+
$params = [
2838+
'itemtype' => Computer::class,
2839+
];
2840+
$params['_idor_token'] = Session::getNewIDORToken(Computer::class, $params);
2841+
$results = \Dropdown::getDropdownValue($params, false);
2842+
2843+
// Assert: only one computer should be count
2844+
$this->assertEquals(count($expected), $results["count"]);
2845+
if (!empty($expected)) {
2846+
$found_items = array_map(
2847+
fn($data) => $data['text'],
2848+
$results["results"][1]["children"],
2849+
);
2850+
$this->assertEquals($expected, $found_items);
2851+
}
2852+
}
27712853
}

0 commit comments

Comments
 (0)