Add role and permission management with enhanced access control
This update introduces functionalities for managing roles, permissions, and access control across departments. Key additions include methods for filtering, restricting, and handling user permissions, as well as new APIs for assigning/removing permissions to/from roles. Access to resources like orders, bookings, and plate scans is now securely tied to department-specific permissions.
This commit is contained in:
@@ -2,8 +2,10 @@
|
||||
|
||||
namespace classes;
|
||||
|
||||
use Exception;
|
||||
use interfaces\response_i;
|
||||
use JetBrains\PhpStorm\NoReturn;
|
||||
use objects\users_o;
|
||||
|
||||
class response implements response_i
|
||||
{
|
||||
@@ -11,6 +13,7 @@ class response implements response_i
|
||||
private array $data = [];
|
||||
private array $meta = [];
|
||||
private array $includes = [];
|
||||
private users_o $users_o;
|
||||
|
||||
#[NoReturn] public function success(mixed $data, int $status = null): void
|
||||
{
|
||||
@@ -206,4 +209,19 @@ class response implements response_i
|
||||
error_reporting(0);
|
||||
ini_set('display_errors', '0');
|
||||
}
|
||||
|
||||
/**
|
||||
* This function gets the user object from the token
|
||||
* @throws Exception If the token is invalid, or the user is not found
|
||||
*/
|
||||
public function get_user(): users_o|false
|
||||
{
|
||||
// Check if the user is already set
|
||||
if (!isset($this->users_o)) {
|
||||
// Get the user object
|
||||
$this->users_o = (new authentication())->get_user();
|
||||
}
|
||||
// Return the user object
|
||||
return $this->users_o;
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,64 @@ class groups_o extends db
|
||||
self::setTable('groups');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the group by name
|
||||
* @throws Exception If an object is not selected
|
||||
*/
|
||||
public function asArray(): array
|
||||
{
|
||||
self::requireSelected();
|
||||
return [
|
||||
'id' => (int)$this->id,
|
||||
'name' => (string)$this->name->value(),
|
||||
'description' => (string)$this->description->value(),
|
||||
'created_at' => (string)$this->created_at->value(),
|
||||
'permissions' => $this->getPermissions()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all permissions for a group
|
||||
* @return array
|
||||
* @throws Exception If the group was not selected
|
||||
*/
|
||||
public function getPermissions(): array
|
||||
{
|
||||
self::requireSelected();
|
||||
return (new groups_permissions_o())->getGroupPermissions($this->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get department ids, that the group has permission (department_access_:group_id) to access
|
||||
* @return array An array of department ids e.g. [1, 2, 3]
|
||||
* @throws Exception If the group was not selected
|
||||
*/
|
||||
public function getDepartments(): array
|
||||
{
|
||||
self::requireSelected();
|
||||
$permissions = (new groups_permissions_o())->getGroupPermissionsMatching($this->id, '/^department_access_[0-9]+$/');
|
||||
$departments = [];
|
||||
foreach ( $permissions as $value ) {
|
||||
$tmp = explode('_', $value['permission']);
|
||||
$departments[] = (int)$tmp[2];
|
||||
}
|
||||
return $departments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a permission to a group
|
||||
*
|
||||
* @param string $permission_id
|
||||
* @return self $this
|
||||
* @throws Exception If the permission was not added successfully
|
||||
*/
|
||||
public function addPermission(string $permission_id): self
|
||||
{
|
||||
self::requireSelected();
|
||||
(new groups_permissions_o())->add($this->id, $permission_id);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a group
|
||||
* @param string $name
|
||||
@@ -56,28 +114,16 @@ class groups_o extends db
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the group by name
|
||||
* @throws Exception If an object is not selected
|
||||
* Remove a permission from a group
|
||||
*
|
||||
* @param string $permission_id
|
||||
* @return self $this
|
||||
* @throws Exception If the permission was not removed successfully
|
||||
*/
|
||||
public function asArray(): array
|
||||
public function removePermission(string $permission_id): self
|
||||
{
|
||||
self::requireSelected();
|
||||
return [
|
||||
'id' => (int)$this->id,
|
||||
'name' => (string)$this->name->value(),
|
||||
'description' => (string)$this->description->value(),
|
||||
'created_at' => (string)$this->created_at->value()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all permissions for a group
|
||||
* @return array
|
||||
* @throws Exception If the group was not selected
|
||||
*/
|
||||
public function getPermissions(): array
|
||||
{
|
||||
self::requireSelected();
|
||||
return (new groups_permissions_o())->getGroupPermissions($this->id);
|
||||
(new groups_permissions_o())->remove($this->id, $permission_id);
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -65,6 +65,39 @@ class groups_permissions_o extends db
|
||||
//TODO: Add cache invalidation
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a permission from a group
|
||||
* @param int $group_id
|
||||
* @param string $permission
|
||||
* @return void
|
||||
* @throws Exception If the permission does not exist for the group
|
||||
* @throws Exception If the object was not deleted successfully
|
||||
* @throws Exception If the group does not exist
|
||||
*/
|
||||
public function remove(int $group_id, string $permission): void
|
||||
{
|
||||
// Check if the group exists
|
||||
$group = new groups_o();
|
||||
$group->select($group_id);
|
||||
$group->requireSelected();
|
||||
// Check if the permission exists for the group
|
||||
$isDefined = self::getFieldsWhere([
|
||||
'group_id' => $group_id,
|
||||
'permission' => $permission
|
||||
], ['id']);
|
||||
// Throw an exception if the permission does not exist for the group
|
||||
if (!$isDefined) {
|
||||
throw new Exception('The permission does not exist for the group.');
|
||||
}
|
||||
// Get the id of the permission entry in the database
|
||||
$id = $isDefined[0]['id'];
|
||||
// Remove the permission
|
||||
$tmp_id = new groups_permissions_o();
|
||||
$tmp_id->select((int)$id);
|
||||
$tmp_id->requireSelected();
|
||||
$tmp_id->delete();
|
||||
}
|
||||
|
||||
public function asArray(): array
|
||||
{
|
||||
return [
|
||||
@@ -85,4 +118,24 @@ class groups_permissions_o extends db
|
||||
'group_id' => $group_id
|
||||
], ['permission']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all permissions for a group matching a regex
|
||||
* @throws Exception If the group does not exist
|
||||
*/
|
||||
public function getGroupPermissionsMatching(int $id, string $regex): array
|
||||
{
|
||||
// Check if the group exists
|
||||
$group = new groups_o();
|
||||
$group->select($id);
|
||||
$group->requireSelected();
|
||||
// Get the permissions
|
||||
$permissions = self::getFieldsWhere([
|
||||
'group_id' => $id
|
||||
], ['permission']);
|
||||
// Filter the permissions
|
||||
return array_filter($permissions, function ($permission) use ($regex) {
|
||||
return preg_match($regex, $permission['permission']);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -65,6 +65,7 @@ class orders_o extends db
|
||||
$this->stripe_module_orders = (new stripe_module_orders_o())->select($this->id);
|
||||
$this->deleted_at = new object_property($this->table, $this->id, 'deleted_at', 'timestamp', false);
|
||||
}
|
||||
|
||||
|
||||
public function includeIncludes(): orders_o
|
||||
{
|
||||
|
||||
@@ -41,7 +41,12 @@ class bookingsRoute
|
||||
function ($booking) {
|
||||
$booking['department'] = (int)$booking['department'];
|
||||
return $booking;
|
||||
}
|
||||
},
|
||||
$bookings_o->forceRestrictFilters(
|
||||
[
|
||||
'department' => $user->getGroup()->getDepartments(),
|
||||
]
|
||||
)
|
||||
))
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -297,8 +297,6 @@ class departmentsRoute
|
||||
// Check if the required fields are set
|
||||
self::requireParameters(['id']);
|
||||
self::requireType((int)self::getParameter('id'), self::TYPE_INT());
|
||||
// Check if the user has access to the order
|
||||
self::requireOrderAccess((int)self::getParameter('id'));
|
||||
// Get the order
|
||||
$order = (new orders_o())->select((int)self::getParameter('id'));
|
||||
$order->requireSelected();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace routes;
|
||||
|
||||
use classes\authentication;
|
||||
use classes\response;
|
||||
use objects\logs_o;
|
||||
use objects\orders_o;
|
||||
use traits\route_t;
|
||||
@@ -15,25 +16,30 @@ class orderRoute
|
||||
{
|
||||
$this->get('/order', function () {
|
||||
// Require the user to be logged in
|
||||
global $response;
|
||||
global /** @var response $response */
|
||||
$response;
|
||||
$this->requirePermission('fetch_order');
|
||||
// Get the user object
|
||||
$user = (new authentication())->get_user();
|
||||
$user = $response->get_user();
|
||||
// Check if the request was successful
|
||||
if ($user) {
|
||||
// Make sure the order id is set
|
||||
if (!(int)$this->fromRequest('id')) {
|
||||
$response->error('Order id is required', 400);
|
||||
}
|
||||
$orders_o = new orders_o();
|
||||
|
||||
// Make sure the order exists
|
||||
if (!(new orders_o())->getOrderById($this->fromRequest('id'))->exists()) {
|
||||
if (!$orders_o->getOrderById($this->fromRequest('id'))->exists()) {
|
||||
$response->error('Order not found', 400);
|
||||
}
|
||||
// Log the incident
|
||||
(new logs_o())->add('orders', 'global', 1, $user->id, 'FETCH_ORDER', 'Successfully fetched order');
|
||||
// Check if the user has access to the department the order is in
|
||||
$this->requirePermission('department_access_' . $orders_o->department_id->value());
|
||||
// Return the list of departments
|
||||
$response->success(
|
||||
(new orders_o())->getOrderById($this->fromRequest('id'))->includeIncludes()->asArray()
|
||||
$orders_o->getOrderById($this->fromRequest('id'))->includeIncludes()->asArray()
|
||||
);
|
||||
} else {
|
||||
// Log the incident
|
||||
@@ -43,7 +49,8 @@ class orderRoute
|
||||
}
|
||||
},
|
||||
[
|
||||
'fetch_order' => 'Fetch order'
|
||||
'fetch_order' => 'Fetch any order, provided the user has access to the department the order is in',
|
||||
'department_access_:id' => 'Access to the department the order is in'
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
@@ -30,9 +30,10 @@ class ordersRoute
|
||||
(new logs_o())->add('orders', 'global', 1, $user->id, 'LIST_ORDERS', 'Successfully listed orders');
|
||||
// Create economic_module_orders object
|
||||
$economic_module_orders = new economic_module_orders();
|
||||
$orders = new orders_o();
|
||||
// Return the list of departments
|
||||
$response->success(
|
||||
(new orders_o())->listObjectsWithPaginationIfSet(
|
||||
$orders->listObjectsWithPaginationIfSet(
|
||||
function ($order) {
|
||||
// Add the invoice status to the order
|
||||
$order['economic_invoice_module'] = (new economic_module_orders())->getByOrderId($order['id'])->asArray();
|
||||
@@ -45,7 +46,13 @@ class ordersRoute
|
||||
$order['customer_name'] = (new users_o())->getCustomerName($order['customer_id']);
|
||||
/** @var array $order */
|
||||
return $order;
|
||||
}
|
||||
},
|
||||
$orders->forceRestrictFilters(
|
||||
[
|
||||
// This makes sure that the user can only see orders from the departments they explicitly have access to
|
||||
'department_id' => $user->getGroup()->getDepartments(),
|
||||
]
|
||||
)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -80,11 +80,18 @@ class plateScansRoute
|
||||
// Check if the request was successful
|
||||
if ($user) {
|
||||
// Get the number plate scans
|
||||
$number_plate_scans = (new plate_scans_o())->listObjectsWithPaginationIfSet();
|
||||
$number_plate_scans = (new plate_scans_o());
|
||||
$result = $number_plate_scans->listObjectsWithPaginationIfSet(null,
|
||||
$number_plate_scans->forceRestrictFilters(
|
||||
[
|
||||
'department_id' => $user->getGroup()->getDepartments()
|
||||
]
|
||||
)
|
||||
);
|
||||
// Log the incident
|
||||
(new logs_o())->add('numberplatescans', 'global', 1, $user->id, 'LIST_NUMBER_PLATE_SCANS', 'Successfully listed number plate scans');
|
||||
// Return the number plate scans
|
||||
$response->success($number_plate_scans);
|
||||
$response->success($result);
|
||||
} else {
|
||||
// Log the incident
|
||||
(new logs_o())->add('numberplatescans', 'global', 1, 0, 'LIST_NUMBER_PLATE_SCANS', 'No user found, or invalid session');
|
||||
@@ -92,7 +99,8 @@ class plateScansRoute
|
||||
$response->error('Invalid session', 400);
|
||||
}
|
||||
}, [
|
||||
'list_number_plate_scans' => 'List all number plate scans'
|
||||
'list_number_plate_scans' => 'List all number plate scans, provided the user has access to the department the number plate scan is in',
|
||||
'department_access_:id' => 'Access to the department the number plate scan is in'
|
||||
]);
|
||||
|
||||
self::get('/numberplatescans/post', function () {
|
||||
|
||||
@@ -21,13 +21,22 @@ class rolesRoute
|
||||
if ($user) {
|
||||
(new logs_o())->add('roles', 'global', 1, 0, 'ROLES', 'User accessed the roles list');
|
||||
$groups = new groups_o();
|
||||
// Check if the id parameter is set
|
||||
if (self::isParametersSet(['id'])) {
|
||||
self::requireType((int)self::getParameter('id'), self::type_int());
|
||||
$groups->select((int)self::getParameter('id'));
|
||||
$groups->requireSelected();
|
||||
$response->success($groups->asArray());
|
||||
}
|
||||
// Return the list of roles
|
||||
$response->success($groups->listObjectsWithPaginationIfSet(
|
||||
function ($group) {
|
||||
return [
|
||||
'id' => (int)$group['id'],
|
||||
'name' => (string)$group['name'],
|
||||
'description' => (string)$group['description'],
|
||||
'created_at' => (string)$group['created_at']
|
||||
'created_at' => (string)$group['created_at'],
|
||||
'permissions' => (new groups_o())->select((int)$group['id'])->getPermissions()
|
||||
];
|
||||
}
|
||||
));
|
||||
@@ -96,5 +105,57 @@ class rolesRoute
|
||||
'edit_role' => 'Edit a role'
|
||||
]
|
||||
);
|
||||
|
||||
self::post('/roles/permissions', function () {
|
||||
// Require the user to be logged in
|
||||
global $response;
|
||||
self::requirePermission('add_role_permission');
|
||||
$user = (new authentication())->get_user();
|
||||
if ($user) {
|
||||
self::requireParameters(['group_id', 'permission_id']);
|
||||
self::requireType((int)self::getParameter('group_id'), self::type_int());
|
||||
self::requireType((string)self::getParameter('permission_id'), 'string');
|
||||
$group = new groups_o();
|
||||
$group->select((int)self::getParameter('group_id'));
|
||||
$group->requireSelected();
|
||||
$group->addPermission((string)self::getParameter('permission_id'));
|
||||
(new logs_o())->add('roles', 'global', 1, $user->id, 'ROLES', 'User added a permission to a role');
|
||||
$response->success($group->asArray());
|
||||
} else {
|
||||
(new logs_o())->add('roles', 'global', 0, 0, 'ROLES', 'User tried to add a permission to a role without a valid session');
|
||||
$response->error('Invalid session', 400);
|
||||
}
|
||||
},
|
||||
[
|
||||
'add_role_permission' => 'Add a permission to a role'
|
||||
]
|
||||
);
|
||||
|
||||
self::delete('/roles/permissions', function () {
|
||||
// Require the user to be logged in
|
||||
global $response;
|
||||
self::requirePermission('delete_role_permission');
|
||||
$user = (new authentication())->get_user();
|
||||
if ($user) {
|
||||
self::requireParameters(['group_id', 'permission_id']);
|
||||
self::requireType((int)self::getParameter('group_id'), self::type_int());
|
||||
self::requireMinLength('group_id', 1);
|
||||
self::requireType((string)self::getParameter('permission_id'), 'string');
|
||||
self::requireMinLength('permission_id', 1);
|
||||
$group = new groups_o();
|
||||
$group->select((int)self::getParameter('group_id'));
|
||||
$group->requireSelected();
|
||||
$group->removePermission((string)self::getParameter('permission_id'));
|
||||
(new logs_o())->add('roles', 'global', 1, $user->id, 'ROLES', 'User deleted a permission from a role');
|
||||
$response->success($group->asArray());
|
||||
} else {
|
||||
(new logs_o())->add('roles', 'global', 0, 0, 'ROLES', 'User tried to delete a permission from a role without a valid session');
|
||||
$response->error('Invalid session', 400);
|
||||
}
|
||||
},
|
||||
[
|
||||
'delete_role_permission' => 'Remove a permission from a role'
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ class userRoute
|
||||
}
|
||||
},
|
||||
[
|
||||
'get_user' => 'Get user'
|
||||
'get_user' => 'Get a user by ID'
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
@@ -195,7 +195,8 @@ class usersRoute
|
||||
);
|
||||
},
|
||||
[
|
||||
'list_public_employees' => 'List all public employees'
|
||||
'list_public_employees' => 'List all public employees',
|
||||
'employee_public_data' => 'When this permission is set, the user is PUBLICLY visible on the employee login page'
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -131,16 +131,37 @@ trait db_object_t
|
||||
|
||||
/**
|
||||
* List objects with pagination (if set)
|
||||
* @param callable|null $parseFunction The function to parse the objects
|
||||
* @param string|null $forcedFilters The filters to force on the objects (Use the array_to_filters function to convert an array to a string)
|
||||
* @return array The list of objects in the table
|
||||
* @throws Exception
|
||||
*/
|
||||
public function listObjectsWithPaginationIfSet($parseFunction = null): array
|
||||
public function listObjectsWithPaginationIfSet($parseFunction = null, $forcedFilters = null): array
|
||||
{
|
||||
// Link to the listObjectsWithPaginationIfSet function.
|
||||
return self::db_object_t__listObjectsWithPaginationIfSet($parseFunction, $forcedFilters);
|
||||
}
|
||||
|
||||
/**
|
||||
* List objects with pagination (if set)
|
||||
* @note This is separate from listObjectsWithPaginationIfSet, as it is used in the API routes with specific permission checks
|
||||
* @param callable|null $parseFunction The function to parse the objects
|
||||
* @param string|null $forcedFilters The filters to force on the objects (Use the array_to_filters function to convert an array to a string)
|
||||
* @return array
|
||||
* @throws Exception If the user does not have permission to list the objects
|
||||
*/
|
||||
public function db_object_t__listObjectsWithPaginationIfSet($parseFunction = null, $forcedFilters = null): array
|
||||
{
|
||||
global $response;
|
||||
$page = ((int)$response->getRequestParameter('page')) ?? null; // Get the page number
|
||||
$limit = ((int)$response->getRequestParameter('limit')) ?? null; // Get the number of objects per page
|
||||
$search = $response->getRequestParameter('search') ?? null; // Get the search query
|
||||
$filters = $response->getRequestParameter('filters') ?? null; // Get the filters ( Eg. department_id:1,role_id:2 OR department_id:1 )
|
||||
// If the forced filters are set, use them
|
||||
if ($forcedFilters) {
|
||||
$filters = $forcedFilters;
|
||||
} else {
|
||||
$filters = $response->getRequestParameter('filters') ?? null; // Get the filters ( Eg. department_id:1,role_id:2 OR department_id:1 )
|
||||
}
|
||||
$order = $response->getRequestParameter('order') ?? null; // Get the order ( Eg. name:ASC )
|
||||
// Make the filters an array
|
||||
if ($filters) {
|
||||
@@ -148,6 +169,18 @@ trait db_object_t
|
||||
$temp = [];
|
||||
foreach ( $filters as $filter ) {
|
||||
$filter = explode(':', $filter);
|
||||
// Check if the filter already exists, if it does, make it an array
|
||||
if (isset($temp[$filter[0]])) {
|
||||
if (!is_array($temp[$filter[0]])) {
|
||||
$temp[$filter[0]] = [$temp[$filter[0]]];
|
||||
}
|
||||
// Check if the filter already exists within the array with the same key, if it does, skip it
|
||||
if (in_array($filter[1], $temp[$filter[0]])) {
|
||||
continue;
|
||||
}
|
||||
$temp[$filter[0]][] = $filter[1];
|
||||
continue;
|
||||
}
|
||||
$temp[$filter[0]] = $filter[1];
|
||||
}
|
||||
$filters = $temp;
|
||||
@@ -188,11 +221,13 @@ trait db_object_t
|
||||
|
||||
// Fetch table fields to search
|
||||
$fields = $this->searchableFields;
|
||||
$fieldTypes = [];
|
||||
if (empty($fields)) {
|
||||
$fields = [];
|
||||
$result = $mysqli->query("SHOW COLUMNS FROM {$this->table}");
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$fields[] = $row['Field'];
|
||||
$fieldTypes[$row['Field']] = $row['Type'];
|
||||
}
|
||||
$result->free();
|
||||
}
|
||||
@@ -220,6 +255,25 @@ trait db_object_t
|
||||
if (!empty($filters)) {
|
||||
foreach ( $filters as $field => $value ) {
|
||||
if (in_array($field, $fields)) {
|
||||
// If the value is an array, add multiple filters
|
||||
if (is_array($value)) {
|
||||
$temp = [];
|
||||
foreach ( $value as $v ) {
|
||||
// Determine the type of the field
|
||||
$type = $fieldTypes[$field];
|
||||
// If the field is an integer, cast the value to an integer
|
||||
if (str_contains($type, 'int')) {
|
||||
$temp[] = "`$field` = $v";
|
||||
} else {
|
||||
$temp[] = "`$field` = ?";
|
||||
$params[] = $v;
|
||||
}
|
||||
//$temp[] = "`$field` = ?";
|
||||
//$params[] = $v;
|
||||
}
|
||||
$whereClauses[] = '(' . implode(' OR ', $temp) . ')';
|
||||
continue;
|
||||
}
|
||||
$whereClauses[] = "`$field` = ?";
|
||||
$params[] = $value;
|
||||
}
|
||||
@@ -318,6 +372,109 @@ trait db_object_t
|
||||
return $objects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forcefully add or override filters to the user's request
|
||||
* @param string $filterString The filters to forcefully override/add on the objects (Eg. 'department_id:1,role_id:2')
|
||||
* @returns string The updated filters string with the forced filters added
|
||||
*/
|
||||
public function forceAddFilters(string $filterString): string
|
||||
{
|
||||
global /** @var response $response */
|
||||
$response;
|
||||
$filters = $response->getRequestParameter('filters') ?? null; // Get the filters ( Eg. department_id:1,role_id:2 OR department_id:1 )
|
||||
// If the filters are set, add the forced filters to the filters
|
||||
if ($filters) {
|
||||
$filters .= ',' . $filterString;
|
||||
} else {
|
||||
$filters = $filterString;
|
||||
}
|
||||
return $filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forcefully restrict filters to the user's request
|
||||
* @param array $fieldsAndValues The filters to forcefully restrict on the objects (Eg. ['department_id' => 1, 'role_id' => [1,2]]) - This would throw an error if the user tries to search for a different department_id or role_id than 1 or 2
|
||||
* @returns string The updated filters string with the forced filters added (Eg. 'department_id:1,role_id:1,role_id:2')
|
||||
* @throws Exception If the user tries to search for a different department_id or role_id than 1 or 2
|
||||
*/
|
||||
public function forceRestrictFilters(array $fieldsAndValues): string
|
||||
{
|
||||
global /** @var response $response */
|
||||
$response;
|
||||
$filters = $response->getRequestParameter('filters') ?? null; // Get the filters ( Eg. department_id:1,role_id:2 OR department_id:1 )
|
||||
// If the filters are set, add the forced filters to the filters
|
||||
if ($filters) {
|
||||
$filters = $this->filter_string_to_array($filters);
|
||||
foreach ( $fieldsAndValues as $field => $value ) {
|
||||
// If the value is an array, check if the user tries to search for a different value than the ones provided
|
||||
if (is_array($value)) {
|
||||
if (!in_array($filters[$field], $value)) {
|
||||
throw new Exception('Invalid filter: ' . $field . ' - ' . $filters[$field] . '. Permitted values: ' . implode(', ', $value));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Check if the user tries to search for a different value than the one provided
|
||||
if ($filters[$field] !== $value) {
|
||||
throw new Exception('Invalid filter: ' . $field . ' - ' . $filters[$field] . '. Permitted value: ' . $value);
|
||||
}
|
||||
}
|
||||
// If any of the forced filters are not set, set them - This is done to prevent the user from searching by omitting the forced filters
|
||||
foreach ( $fieldsAndValues as $field => $value ) {
|
||||
if (!isset($filters[$field])) {
|
||||
$filters[$field] = $value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$filters = $fieldsAndValues;
|
||||
}
|
||||
return $this->array_to_filters($filters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter string to array
|
||||
* @param string $filterString The filter string (Eg. 'department_id:1,role_id:2')
|
||||
* @return array The array of filters (Eg. ['department_id' => 1, 'role_id' => 2])
|
||||
*/
|
||||
public function filter_string_to_array(string $filterString): array
|
||||
{
|
||||
$filters = explode(',', $filterString);
|
||||
$temp = [];
|
||||
foreach ( $filters as $filter ) {
|
||||
$filter = explode(':', $filter);
|
||||
$temp[$filter[0]] = $filter[1];
|
||||
}
|
||||
return $temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an array of filters to a string
|
||||
* @param $array array The array of filters
|
||||
* @return string
|
||||
* @example ['department_id' => [1,2], 'role_id' => 2, 'id' => 1] => 'department_id:1,department_id:2,role_id:2,id:1'
|
||||
*/
|
||||
public function array_to_filters(array $array): string
|
||||
{
|
||||
$filter_string = '';
|
||||
foreach ( $array as $key => $value ) {
|
||||
// If this is not the first filter, add a comma to separate the filters
|
||||
if ($filter_string !== '') {
|
||||
$filter_string .= ',';
|
||||
}
|
||||
// If the value is an array, add multiple filters
|
||||
if (is_array($value)) {
|
||||
foreach ( $value as $v ) {
|
||||
$filter_string .= $key . ':' . $v . ',';
|
||||
}
|
||||
// Remove the last comma
|
||||
$filter_string = rtrim($filter_string, ',');
|
||||
continue;
|
||||
}
|
||||
// Add the filter
|
||||
$filter_string .= $key . ':' . $value;
|
||||
}
|
||||
return $filter_string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total number of objects
|
||||
* @param string|null $search The search query
|
||||
|
||||
@@ -34,7 +34,7 @@ trait route_t
|
||||
// Check if the value is of the required type
|
||||
if ($value_type !== $type) {
|
||||
global $response;
|
||||
$response->error('Invalid type. Expected: ' . $type . ' Got: ' . $value_type, 400);
|
||||
$response->error('Invalid type. Expected: ' . $type . ' Got: ' . $value_type . ' Value: ' . $value, 400);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user