Files
api/services/nginx/app/objects/subuser_grants_o.php
T
Jeppe Bundgaard eef436d44b Add tests for subuser password validation and grant permission normalization
Introduce unit and API tests for subuser password policies ensuring compliance with complexity requirements. Normalize subuser grant permission handling for consistency, including support for legacy zero permissions.
2026-05-27 19:17:19 +02:00

189 lines
6.6 KiB
PHP

<?php
namespace objects;
use classes\db;
use classes\object_property;
use Exception;
use modules\subusers\helpers\subusers_permission_node_key;
use Random\RandomException;
use traits\db_object_t;
class subuser_grants_o extends db
{
use db_object_t;
public object_property $billing_customer_number;
public object_property $subuser;
public object_property $enabled;
public object_property $note;
public object_property $permissions;
public object_property $created_at;
public object_property $updated_at;
public object_property $deleted_at;
const defaultPermissions = [
'VEHICLES_LIST',
'SELFSERVE_ADD',
'BOOKINGS_LIST',
'BOOKINGS_ADD',
'BOOKINGS_EDIT',
'BOOKINGS_DELETE',
];
public static function normalizePermissionsValue(mixed $raw): array
{
if ($raw === null || $raw === '' || $raw === false || $raw === 0 || $raw === '0') {
return [];
}
if ($raw instanceof subusers_permission_node_key) {
return [$raw->name];
}
if (is_array($raw)) {
$permissions = [];
$permissionCandidates = array_is_list($raw)
? $raw
: array_keys(array_filter($raw, static fn ($enabled): bool => (bool)$enabled));
foreach ($permissionCandidates as $permission) {
if ($permission instanceof subusers_permission_node_key) {
$permission = $permission->name;
}
if (!is_string($permission)) {
continue;
}
$permission = strtoupper(trim($permission));
if ($permission !== '' && subusers_permission_node_key::tryFrom($permission) !== null) {
$permissions[] = $permission;
}
}
return array_values(array_unique($permissions));
}
if (is_string($raw)) {
$decoded = json_decode($raw, true);
if (json_last_error() === JSON_ERROR_NONE) {
return self::normalizePermissionsValue($decoded);
}
$permission = strtoupper(trim($raw));
if (subusers_permission_node_key::tryFrom($permission) !== null) {
return [$permission];
}
}
return [];
}
public function structure(): void
{
$this->setTable('subuser_grants');
}
public function objectChanged(): void
{
// No need to invalidate the cache, since the plate_scans object is not cached
}
public function getObjectProperties(): void
{
$this->billing_customer_number = new object_property($this->table, $this->id, 'billing_customer_number', 'int');
$this->subuser = new object_property($this->table, $this->id, 'subuser', 'int');
$this->enabled = new object_property($this->table, $this->id, 'enabled', 'bool');
$this->note = new object_property($this->table, $this->id, 'note', 'string');
$this->permissions = new object_property($this->table, $this->id, 'permissions', 'json');
$this->created_at = new object_property($this->table, $this->id, 'created_at', 'timestamp');
$this->updated_at = new object_property($this->table, $this->id, 'updated_at', 'timestamp');
$this->deleted_at = new object_property($this->table, $this->id, 'deleted_at', 'timestamp');
}
public function asArray(): array
{
return [
'id' => (int)$this->id,
'billing_customer_number' => (int)$this->billing_customer_number->value(),
'subuser' => (int)$this->subuser->value(),
'enabled' => (bool)$this->enabled->value(),
'note' => $this->note->value(),
'permissions' => self::normalizePermissionsValue($this->permissions->value()),
'created_at' => $this->created_at->value(),
'updated_at' => $this->updated_at->value(),
'deleted_at' => $this->deleted_at->value(),
];
}
/**
* Add a new subuser grant to the database.
* @param int $billing_customer_number
* @param int $subuser
* @param bool $enabled
* @param string|null $note
* @param array|null $permissions
* @return $this
* @throws Exception If the object creation fails
*/
public function add(int $billing_customer_number, int $subuser, bool $enabled, ?string $note, ?array $permissions = self::defaultPermissions): subuser_grants_o
{
global $db;
$permissions = self::normalizePermissionsValue($permissions);
$tmp = $this->add_object([
'billing_customer_number' => (int)$billing_customer_number,
'subuser' => (int)$subuser,
'enabled' => (bool)$enabled,
'note' => !empty($note) ? $db->escape_string($note) : null,
'permissions' => json_encode($permissions, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
]);
$this->id = (int)$tmp;
$this->getObjectProperties();
return $this;
}
public function getGrantsForSubuserAndCustomer(int $subuser_id, ?int $customer_number): array
{
$grants = self::getFieldsWhere([
'billing_customer_number' => $customer_number,
'subuser' => $subuser_id,
'enabled' => 1,
'deleted_at' => null,
], ['permissions']);
// Extract permissions from the grants
$permissions = [];
foreach ($grants as $grant) {
$grant_permissions = self::normalizePermissionsValue($grant['permissions'] ?? null);
if (is_array($grant_permissions)) {
$permissions = array_merge($permissions, $grant_permissions);
}
}
return array_values(array_unique($permissions));
}
public function getGrantForSubuserAndCustomer(int $subuser_id, int $customer_number, bool $includeDisabled = true): ?subuser_grants_o
{
$grants = self::getFieldsWhere([
'billing_customer_number' => $customer_number,
'subuser' => $subuser_id,
'deleted_at' => null,
], ['id', 'enabled']);
if (!$includeDisabled) {
$grants = array_values(array_filter($grants, static fn (array $grant): bool => (int)($grant['enabled'] ?? 0) === 1));
}
if (count($grants) === 0) {
return null;
}
usort($grants, static fn (array $left, array $right): int => (int)$right['id'] <=> (int)$left['id']);
$grant = (new subuser_grants_o())->select((int)$grants[0]['id']);
$grant->getObjectProperties();
return $grant;
}
}