Add order bookings functionality with permissions and access control
- Introduced `order_bookings_o` class for interacting with the `order_bookings` database table. - Added `hasDepartmentAccess` method to handle department-level permissions. - Implemented new routes for creating and retrieving order bookings, including pagination support. - Enhanced access control to differentiate between own and departmental bookings based on permissions. - Updated input validation and improved error handling for order bookings.
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace objects;
|
||||
|
||||
use classes\db;
|
||||
use classes\object_property;
|
||||
use Exception;
|
||||
use traits\db_object_t;
|
||||
|
||||
class order_bookings_o extends db
|
||||
{
|
||||
use db_object_t;
|
||||
|
||||
public object_property $customer_number;
|
||||
public object_property $department;
|
||||
public object_property $reg_1;
|
||||
public object_property $reg_2;
|
||||
public object_property $reg_3;
|
||||
public object_property $datetime;
|
||||
public object_property $note;
|
||||
public object_property $reference;
|
||||
public object_property $po;
|
||||
public object_property $pickup;
|
||||
public object_property $items;
|
||||
public object_property $created_at;
|
||||
public object_property $updated_at;
|
||||
public object_property $deleted_at;
|
||||
|
||||
|
||||
public function structure(): void
|
||||
{
|
||||
$this->setTable('order_bookings');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an object
|
||||
* @param array $data The additional data of the object (e.g. ["key" => "value"])
|
||||
* @return void
|
||||
* @throws Exception If the object was not created successfully
|
||||
*/
|
||||
public function add(
|
||||
array $data,
|
||||
): void
|
||||
{
|
||||
global /** @var db $db */
|
||||
$db;
|
||||
// Add the object to the database
|
||||
$new_id = self::add_object($data);
|
||||
self::select($new_id);
|
||||
}
|
||||
|
||||
public function getObjectProperties(): void
|
||||
{
|
||||
$this->customer_number = new object_property($this->table, $this->id, 'customer_number', 'int', false);
|
||||
$this->department = new object_property($this->table, $this->id, 'department', 'int', false);
|
||||
$this->reg_1 = new object_property($this->table, $this->id, 'reg_1', 'string', false);
|
||||
$this->reg_2 = new object_property($this->table, $this->id, 'reg_2', 'string', false);
|
||||
$this->reg_3 = new object_property($this->table, $this->id, 'reg_3', 'string', false);
|
||||
$this->datetime = new object_property($this->table, $this->id, 'datetime', 'timestamp', false);
|
||||
$this->note = new object_property($this->table, $this->id, 'note', 'string', false);
|
||||
$this->reference = new object_property($this->table, $this->id, 'reference', 'string', false);
|
||||
$this->po = new object_property($this->table, $this->id, 'po', 'string', false);
|
||||
$this->pickup = new object_property($this->table, $this->id, 'pickup', 'bool', false);
|
||||
$this->items = new object_property($this->table, $this->id, 'items', 'json', false);
|
||||
$this->created_at = new object_property($this->table, $this->id, 'created_at', 'timestamp', false);
|
||||
$this->updated_at = new object_property($this->table, $this->id, 'updated_at', 'timestamp', false);
|
||||
$this->deleted_at = new object_property($this->table, $this->id, 'deleted_at', 'timestamp', false);
|
||||
}
|
||||
|
||||
public function objectChanged(): void
|
||||
{
|
||||
//TODO: Add cache invalidation
|
||||
}
|
||||
|
||||
|
||||
public function asArray(): array
|
||||
{
|
||||
return [
|
||||
'id' => (int)$this->id,
|
||||
'customer_number' => (int)$this->customer_number->value(),
|
||||
'department' => (int)$this->department->value(),
|
||||
'reg_1' => $this->reg_1->value(),
|
||||
'reg_2' => $this->reg_2->value(),
|
||||
'reg_3' => $this->reg_3->value(),
|
||||
'datetime' => $this->datetime->value(),
|
||||
'note' => $this->note->value(),
|
||||
'reference' => $this->reference->value(),
|
||||
'po' => $this->po->value(),
|
||||
'pickup' => (bool)$this->pickup->value(),
|
||||
'items' => $this->items->value(),
|
||||
'created_at' => $this->created_at->value(),
|
||||
'updated_at' => $this->updated_at->value(),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,6 +6,7 @@ use classes\authentication;
|
||||
use Exception;
|
||||
use objects\departments_o;
|
||||
use objects\logs_o;
|
||||
use objects\order_bookings_o;
|
||||
use objects\order_items_o;
|
||||
use objects\users_o;
|
||||
use traits\route_t;
|
||||
@@ -34,10 +35,10 @@ class orderBookingRoute
|
||||
$pickup = self::getTargetPickup(); // Bool | Null
|
||||
$items = self::getTargetItems(); // Array of order_items_o objects
|
||||
/**
|
||||
* Debug input
|
||||
* Input data
|
||||
*/
|
||||
$response->success([
|
||||
'customer_number' => $customer_number->customer_number,
|
||||
$data = [
|
||||
'customer_number' => (int)$customer_number->customer_number->value(),
|
||||
'department' => $department->id,
|
||||
'reg_1' => $reg_1,
|
||||
'reg_2' => $reg_2,
|
||||
@@ -48,46 +49,98 @@ class orderBookingRoute
|
||||
'po' => $po,
|
||||
'pickup' => $pickup,
|
||||
'items' => $items,
|
||||
'items_count' => count($items),
|
||||
]);
|
||||
exit;
|
||||
|
||||
//self::requirePermission('user_security_change_email');
|
||||
$user = (new authentication())->get_user();
|
||||
if (!$user) {
|
||||
(new logs_o())->add('user_security', 'global', 0, 0, 'USER_SECURITY_CHANGE_EMAIL', 'User not logged in');
|
||||
$response->error('Invalid session', 400);
|
||||
}
|
||||
// Require the email, and password parameters
|
||||
self::requireParameters(['email', 'password']);
|
||||
// Check if the email is valid
|
||||
$email = (string)self::getParameter('email');
|
||||
self::requireMinLength('email', 5);
|
||||
self::requireMaxLength('email', 255);
|
||||
self::requireType($email, self::type_string());
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
(new logs_o())->add('user_security', 'global', 0, $user->id, 'USER_SECURITY_CHANGE_EMAIL', 'Invalid email');
|
||||
$response->error('Invalid email', 400);
|
||||
}
|
||||
// Validate the password
|
||||
$password = (string)self::getParameter('password');
|
||||
self::requireMinLength('password', 5);
|
||||
self::requireMaxLength('password', 255);
|
||||
self::requireType($password, self::type_string());
|
||||
if (!$user->passwordMatches($password)) {
|
||||
(new logs_o())->add('user_security', 'global', 0, $user->id, 'USER_SECURITY_CHANGE_EMAIL', 'Invalid password');
|
||||
$response->error('Invalid password', 400);
|
||||
} else {
|
||||
// Change the email
|
||||
$user->setEmail($email);
|
||||
(new logs_o())->add('user_security', 'global', 0, $user->id, 'USER_SECURITY_CHANGE_EMAIL', 'Email changed');
|
||||
$response->success(['message' => 'Email changed']);
|
||||
}
|
||||
];
|
||||
/**
|
||||
* Create the object
|
||||
*/
|
||||
$order_bookings_o = new order_bookings_o();
|
||||
$order_bookings_o->add($data);
|
||||
$response->success($order_bookings_o->asArray());
|
||||
},
|
||||
[
|
||||
'user_security_change_email' => 'Change the email address of the user',
|
||||
// No Permissions required.
|
||||
]
|
||||
);
|
||||
|
||||
$this->get('/order-bookings', function () {
|
||||
// Require the user to be logged in
|
||||
global $response;
|
||||
/**
|
||||
* Parameters
|
||||
*/
|
||||
$object = self::getTargetObject();
|
||||
/**
|
||||
* Authentication
|
||||
*/
|
||||
$user = (new authentication())->get_user();
|
||||
/**
|
||||
* Permissions
|
||||
*/
|
||||
$permission_own = 'list_own_bookings'; // Only permits access to bookings with the user's customer number
|
||||
$permission_other = 'list_bookings'; // Requires access to the department as an admin to view other users' bookings
|
||||
$has_permission_own = self::hasPermission($permission_own);
|
||||
$has_permission_other = self::hasPermission($permission_other);
|
||||
$has_permission = false; // Updated below
|
||||
// Check if the user has permission to view their own bookings
|
||||
if ($has_permission_own && (!$object || (int)$object->customer_number->value() === (int)$user->customer_number->value())) {
|
||||
$has_permission = true;
|
||||
}
|
||||
// Check if the user has permission to view other users' bookings
|
||||
if (!$has_permission && $has_permission_other) {
|
||||
if (!empty($object)) self::requireDepartmentAccess((int)$object->department->value());
|
||||
// Check if the user has access to the department
|
||||
$has_permission = true;
|
||||
}
|
||||
// If the user does not have permission, return an error
|
||||
if (!$has_permission) {
|
||||
$response->error('You do not have permission to view this order booking.', 403);
|
||||
}
|
||||
/**
|
||||
* Return the object
|
||||
*/
|
||||
if (!empty($object)) $response->success($object->asArray());
|
||||
/**
|
||||
* Return the filtered list of objects
|
||||
*/
|
||||
$object = new order_bookings_o(); // New object for listing
|
||||
$response->success($object->listObjectsWithPaginationIfSet(
|
||||
function ($booking) { return (new order_bookings_o())->select((int)$booking['id'])->asArray(); },
|
||||
$object->forceRestrictFilters([
|
||||
...($has_permission_other ? [
|
||||
'department' => $user->getGroup()->getDepartments()
|
||||
] : []),
|
||||
...(!$has_permission_other ? [
|
||||
'customer_number' => (int)$user->customer_number->value()
|
||||
] : [])
|
||||
])
|
||||
));
|
||||
},
|
||||
[
|
||||
'list_own_bookings' => 'Permission to list own order bookings.',
|
||||
'list_bookings' => 'Permission to list department order bookings.',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
private function getTargetObject(): order_bookings_o|null
|
||||
{
|
||||
global $response;
|
||||
$parameter = 'id';
|
||||
$error = 'Invalid ' . $parameter;
|
||||
if (!$this->isParametersSet([$parameter])) return null;
|
||||
self::requireParameters(['id']);
|
||||
self::requireType(self::getParameter($parameter), self::type_int());
|
||||
self::requireMinLength($parameter, 1);
|
||||
self::requireMaxLength($parameter, 9);
|
||||
$value = (int)self::getParameter($parameter);
|
||||
self::requireMinValue($value, 1);
|
||||
self::requireMaxValue($value, 999999999);
|
||||
$object = (new order_bookings_o())->select($value);
|
||||
if (!$object->exists()) $response->error($error, 400);
|
||||
return $object;
|
||||
}
|
||||
|
||||
private function getTargetCustomer(): users_o
|
||||
@@ -103,9 +156,7 @@ class orderBookingRoute
|
||||
self::requireMinValue($value, 1);
|
||||
self::requireMaxValue($value, 999999999);
|
||||
$object = (new users_o())->getUserByCustomerNumber($value);
|
||||
if (!$object->exists()) {
|
||||
$response->error($error, 400);
|
||||
}
|
||||
if (!$object->exists()) $response->error($error, 400);
|
||||
return $object;
|
||||
}
|
||||
|
||||
@@ -125,9 +176,7 @@ class orderBookingRoute
|
||||
self::requireMinValue($value, 1);
|
||||
self::requireMaxValue($value, 999999999);
|
||||
$object = (new departments_o())->select($value);
|
||||
if (!$object->exists()) {
|
||||
$response->error($error, 400);
|
||||
}
|
||||
if (!$object->exists()) $response->error($error, 400);
|
||||
return $object;
|
||||
}
|
||||
|
||||
|
||||
@@ -217,6 +217,18 @@ trait route_t
|
||||
self::requirePermission('department_access_' . $department . ($permission ? '_' . $permission : ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* Has department access?
|
||||
* @note This checks if the user has access to the department by checking if the user has the permission department_access_{department} (_{permission} if provided)
|
||||
* @param string $department
|
||||
* @param string|null $permission
|
||||
* @return bool
|
||||
*/
|
||||
public function hasDepartmentAccess(string $department, string|null $permission = null): bool
|
||||
{
|
||||
return self::hasPermission('department_access_' . $department . ($permission ? '_' . $permission : ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* Require permission
|
||||
* @param string $permission
|
||||
|
||||
Reference in New Issue
Block a user